IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Erreur de compilation incompréhensible


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre Expert

    Homme Profil pro
    Ingénieur calcul scientifique
    Inscrit en
    Mars 2013
    Messages
    1 229
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur calcul scientifique

    Informations forums :
    Inscription : Mars 2013
    Messages : 1 229
    Par défaut Erreur de compilation incompréhensible
    Bonjour à tous.

    Ci dessus je vous présente un bout de code bateau divisé en 3 fichiers:
    main.cc
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    #include <iostream>
    #include <string>
     
    #include "MaClasse.h"
     
    int main( int argc, char* argv[] ){
    		MaClasse obj(30,20);
    		std::cout << "Hello World!" << std::endl;
    }
    MaClasse.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <iostream>
     
    int Somme(int a, int b);
     
    class MaClasse {
    public:
    		MaClasse(int x, int y);
    		~MaClasse(){};
     
    private:
    		int _x,_y;
    };
    MaClasse.cc
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    #include "Maclasse.h"
     
    int Somme(int a, int b){return a+b;}
     
    MaClasse::MaClasse(int x, int y){std::cout << "Hey!" << std::endl; _x=x;_y=y;}
    En compilant ceci (sous MAC) avec la commande:
    c++ main.cc MaClasse.cc
    là pas de problème.

    Si maintenant, au lieu de définir ma fonction Somme dans le .cc je mets la définition directement dans le header, mes fichiers MaClasse .h et .cc deviennent :
    MaClasse.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <iostream>
     
    int Somme(int a, int b){return a+b;};
     
    class MaClasse {
    public:
    		MaClasse(int x, int y);
    		~MaClasse(){};
     
    private:
    		int _x,_y;
    };
    MaClasse.cc
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    #include "Maclasse.h"
     
    MaClasse::MaClasse(int x, int y){std::cout << "Hey!" << std::endl; _x=x;_y=y;}
    Et là à la compilation j'ai l'erreur suivante :
    duplicate symbol __Z5Sommeii in:
    /var/folders/_0/4j2nn15x44s9cys_2hyct4vc0000gq/T/main-3bc424.o
    /var/folders/_0/4j2nn15x44s9cys_2hyct4vc0000gq/T/MaClasse-6ec5ac.o
    ld: 1 duplicate symbol for architecture x86_64
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    Quelqu'un pourrait-il m'éclairer ?

    Merci

  2. #2
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    C'est un détail méconnu.

    Ton header est inclus dans main.cc et MaClasse.cc.
    Chacun de ses fichiers sources est compilé en une "unité de compilation", et deviennent main.o et MaClasse.o (ou autre dénomination windowsienne)

    comme le code de la fonction est dans MaClasse.h, il est présent dans main.o et dans MaClasse.o

    Du coup, à l'édition de lien, qui est en gros une mise bout à bout des .o, tu as deux fois le même code.
    C'est précisément le message indiqué par le linker (ld):
    Citation Envoyé par ld
    duplicate symbol __Z5Sommeii
    ...
    ld: 1 duplicate symbol
    Tu remarqueras au passage le nom du symbole, qui Sommeii qui signifie Somme, prenant deux arguments: un int, puis un int.

    C'est une violation de la règle de la définition unique:
    Un symbole utilisé doit être défini une et une seule fois.
    Il n'y a que deux solutions:
    • ne définir qu'une seule fois le symbole
    • séparer le symbole en autant de symbole distincts que nécessaire (ici deux: un pour main.o et un pour maclasse.o)


    La première solution consiste à déclarer la fonction dans le .h, mais ne la définir que dans MaClasse.o
    La seconde solution consiste à marquer explicitement la fonction comme "privée à l'unité de compilation".
    La troisième solution, intermédiaire, et probablement meilleure, permet de ne pas payer ce cout en définissant la fonction comme inlineLa raison pour laquelle tu n'as pas ce problème dans une classe, c'est parce que toutes les fonctions membres définies dans la déclaration de la classe sont automatiquement définies comme inline.

    La solution inline se présente ainsi:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    inline int Somme(int a, int b) {
        return a+b;
    }
    //...
    Une variante du problème existe quand on définit une fonction membre après la déclaration.
    La solution inline se présente ainsi:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    struct Bidule {
        int one() const {return 1;}
        int two() const;
    }
    inline int Bidule::two() const {return 2;}
    Enfin, il est bon de savoir qu'une template doit être intégralement inline, et donc sa définition intégralement incluse dans l'unité de compilation concernée.
    C'est pour cela que les templates sont intégralement définies dans leur en-tête.
    C'est aussi pour cela que la STL est header-only.

  3. #3
    Membre Expert

    Homme Profil pro
    Ingénieur calcul scientifique
    Inscrit en
    Mars 2013
    Messages
    1 229
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur calcul scientifique

    Informations forums :
    Inscription : Mars 2013
    Messages : 1 229
    Par défaut
    Merci pour vos réponses rapides et complètes

    Citation Envoyé par leternel Voir le message
    Il n'y a que deux solutions:
    • ne définir qu'une seule fois le symbole
    • séparer le symbole en autant de symbole distincts que nécessaire (ici deux: un pour main.o et un pour maclasse.o)


    La première solution consiste à déclarer la fonction dans le .h, mais ne la définir que dans MaClasse.o
    La seconde solution consiste à marquer explicitement la fonction comme "privée à l'unité de compilation".
    La troisième solution, intermédiaire, et probablement meilleure, permet de ne pas payer ce cout en définissant la fonction comme inline
    La première solution consiste à déclarer la fonction dans le .h, mais ne la définir que dans MaClasse.o <-- Tu ne veux pas dire plutot MAClasse.cc ?
    Car définir qqch dans un .o, j'ai jamais fait ni entendu parler...

    Effectivement avec le inline, ça fonctionne.
    Du coup, dois-je prendre l'habitude de mettre inline devant toute fonction définie dans un header en dehors d'une classe ou bien est-ce plus subtil que cela ?

    Merci à vous

  4. #4
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    C'est une bonne solution.
    Cependant, ça n'est bon que lorsque la fonction reste lisible, sinon, de toute façon, la fonction sera trop complexe, et le compilateur ne l'inlinera pas.

    Personnellement, je me limite aux fonctions limités à un return.
    Ca peut éventuellement étendu aux fonctions en une ligne.
    par exemple les surcharge de operator<<(ostream&, type const&).

    Pour le reste, je mets la fonction dans le fichier d'implémentation.


    Quant à la différence entre .cc et .o, il n'y en a quasiment aucune.
    Comme tu n'inclue que des .h, chaque .o est le compilé d'un unique .cc.

    J'en profite pour te signaler qu'en général, on utilise l'extension .cpp plutôt que .cc.
    Et certains (moi inclus) utilise .hpp pour les en-têtes C++.

  5. #5
    Invité
    Invité(e)
    Par défaut
    Bonjour,
    Pour ce genre de problème, j'utilise des directives de compilation. Exemple simple, je doit écrire une fichier header qui s'appelle divers.h. Il aura cette forme

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    #ifndef divers_H
    #define divers_H
    /*
    les prototypes qu'on veut
    */
    #endif
    Je pense que cette écriture est tout à fait standard.
    Dernière modification par Invité ; 02/09/2014 à 14h29. Motif: Ajout des balises [CODE] mon code [/CODE] (bouton #)

  6. #6
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    Ca ne protège pas du même problème.

    inline ou le namespace anonyme protègent contre les doubles définitions entre unités différentes.

    La garde protège contre la double inclusion dans la même unité de compilation.
    Ca protège donc contre les erreurs de compilations (multiple definition of ... / previous definition at ...)


    la garde est utile par exemple quand tu écris
    Code Bidule.hpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    #include <string>
    std::string version();

    Code Bidule.hpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    #include "Bidule.hpp"
    #include <string>
    std::string version() {return "ALPHA";}

    Ici, <string> est inclus deux fois, mais comme il y a une garde à l'intérieur, le code contenu n'est écrit qu'une seule fois par le préprocesseur, et donc compilé qu'une seule fois.

    D'ailleurs, c'est pour ca que dans ton .h, tu as précisé que tu mets les prototypes que tu veux. C'est à dire des déclarations de fonctions, et pas leurs définitions.

  7. #7
    Membre Expert

    Homme Profil pro
    Ingénieur calcul scientifique
    Inscrit en
    Mars 2013
    Messages
    1 229
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur calcul scientifique

    Informations forums :
    Inscription : Mars 2013
    Messages : 1 229
    Par défaut
    Merci pour ces compléments très instructifs.

    lg53

  8. #8
    Membre émérite

    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    533
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 533
    Par défaut
    Puisque Somme est une fonction, il existe aussi cette solution-là :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    static int Somme(int a, int b) {
        return a+b;
    }
    static (qui n'a pas la même signification ici que dans une classe) permet de dire au compilo que Somme n'existe que pour l'unité de compilation courante. Donc MaClasse.o détiendra 1 implémentation "locale" de Somme, de même que main.o.

  9. #9
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    C'est la deuxième solution, que je n'avais pas écrite, parce qu'elle n'est normalement pas à utiliser dans un en-tête.

    Il y a techniquement mieux: le namespace anonyme.
    Cela remplit le même rôle, mais pose moins de soucis lors de la recherche de nom.
    En fait, ce static-là (classe de mémorisation) ne devrait jamais être utilisé.

    Le namespace anonyme s'utilise ainsi:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    namespace /*pas de nom ici*/ {
    int Somme(int a, int b) {return a+b;}
    }

    Cependant, l'avantage de la fonction inline, c'est qu'elle est annoncée au compilateur comme candidate au remplacement dans le code.
    Ce qui veut dire que le binaire compilé ne contiendrait même pas la fonction.

    Au lieu d'avoir l'équivalent de ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    //compilation de int a=1, b=2, c = Somme(a, b);
    registre a = 1;
    registre b = 2;
    a sur la stack
    b sur la stack
    appel de __Z5Sommeii
    stack[return] = stack[0] + stack[1]
    return
    registre c depuis la stack
    on aura certainement
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    //compilation de int a=1, b=2, c = Somme(a, b);
    registre a = 1;
    registre b = 2;
    registre c = a + b
    Et si le compilateur estime qu'il ne veut pas inliner la fonction, tout se passe comme s'il créait une autre unité de compilation, rien que pour elle.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Incompréhension d'une erreur à la compilation.
    Par BaygonV dans le forum Débuter
    Réponses: 2
    Dernier message: 20/03/2014, 21h23
  2. Erreur de compilation incompréhensible
    Par Tito_28 dans le forum Débuter
    Réponses: 3
    Dernier message: 20/11/2011, 12h13
  3. Erreur de compilation incompréhensible
    Par javass dans le forum Langage
    Réponses: 2
    Dernier message: 02/06/2008, 09h54
  4. [2.0] Erreur de compilation incompréhensible
    Par strat0 dans le forum Windows Forms
    Réponses: 7
    Dernier message: 17/03/2007, 22h45
  5. Réponses: 2
    Dernier message: 04/03/2003, 23h24

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo