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 :

Problème de linkage


Sujet :

C++

  1. #1
    Membre averti
    Inscrit en
    Décembre 2009
    Messages
    33
    Détails du profil
    Informations forums :
    Inscription : Décembre 2009
    Messages : 33
    Par défaut Problème de linkage
    Bonjour,
    j'essaie pour la premiere fois de compiler en même temps plusieurs fichiers (un cpp par classe et un header), mais j'ai un soucis que je ne sais pas résoudre.

    J'ai pour faire un test rapide une fonction globale appellée "load_image", que j'ai placé dans config.h qui contient aussi mes constantes
    Mon fichier main inclu config.h
    Mon fichier tileset.cpp pareil

    Tous mes fichiers compilent bien, mais au niveau des liens j'ai l'erreur suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1>TilesLayer.obj : error LNK2005: "struct SDL_Surface * __cdecl load_image(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (?load_image@@YAPAUSDL_Surface@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) déjà défini(e) dans main.obj
    que je ne comprend pas trop : a priori j'ai mis des "ifndef .... endif" dans mes includes, donc je comprend pas trop pourquoi est-ce que ma fonction veut se redéfinir à 2 endroits différents.
    Bien sûr si je n'inclut plus config.h dans mon 2eme fichier, la compilation ne marche plus....
    Une idée?

  2. #2
    Membre actif Avatar de Chessmaster1966
    Inscrit en
    Juillet 2010
    Messages
    63
    Détails du profil
    Informations forums :
    Inscription : Juillet 2010
    Messages : 63
    Par défaut
    Peut-être une inclusion multiple de config.h. Je ne sais pas comment tu as déclaré tes directives de compilation ifndef...endif mais essayes ça si ce n'est pas déjà fait :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    #ifndef CONFIG_H
    #define CONFIG_H
     
    ...
     
    #endif

  3. #3
    Membre averti
    Inscrit en
    Décembre 2009
    Messages
    33
    Détails du profil
    Informations forums :
    Inscription : Décembre 2009
    Messages : 33
    Par défaut
    c'est déjà fait, c'est pour ça que je comprends pas trop
    Après j'ai tout fait au feeling, je ne connais pas du tout la méthode d'organisation quand on a plusieurs .cpp à compiler en même temps

  4. #4
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Bonjour,

    lezebulon, quand tu dis :
    Citation Envoyé par lezebulon
    J'ai pour faire un test rapide une fonction globale appellée "load_image", que j'ai placé dans config.h qui contient aussi mes constantes
    On est d'accord que tu parles uniquement de la déclaration de la fonction dans config.h, car sa définition est bien dans config.cpp ?

  5. #5
    Membre averti
    Inscrit en
    Décembre 2009
    Messages
    33
    Détails du profil
    Informations forums :
    Inscription : Décembre 2009
    Messages : 33
    Par défaut
    Euh... non
    En fait j'ai pas de déclaration séparée, juste la définition.
    Et j'ai pas de config.cpp

    En fait je suis parti d'un fichier de base .cpp codé rapido et j'ai essayé de le porter rapido aussi sur plusieurs fichiers, je sais pas trop quelle convention je dois prendre pour tout ce qui est global

  6. #6
    Membre émérite
    Inscrit en
    Juillet 2005
    Messages
    512
    Détails du profil
    Informations forums :
    Inscription : Juillet 2005
    Messages : 512
    Par défaut
    Ta fonction ne serait défini deux fois,
    une fois dans TilesLayer.cpp et
    une fois dans main.cpp

  7. #7
    Membre averti
    Inscrit en
    Décembre 2009
    Messages
    33
    Détails du profil
    Informations forums :
    Inscription : Décembre 2009
    Messages : 33
    Par défaut
    Vu que je met des #ifndef dans mes .h, je comprend pas trop pourquoi justement c'est défini 2 fois... en fait, je pige pas trop globalement ce qu'il se passe entre la compilation, l'édition des liens, etc

  8. #8
    Membre émérite
    Inscrit en
    Juillet 2005
    Messages
    512
    Détails du profil
    Informations forums :
    Inscription : Juillet 2005
    Messages : 512
    Par défaut
    Je disais défini pas déclaré.
    les définitions sont dans les cpp
    Tu n'aurais pas fait un include d'un cpp des fois ?
    Ou une définition dans un h ?

  9. #9
    Membre averti
    Inscrit en
    Décembre 2009
    Messages
    33
    Détails du profil
    Informations forums :
    Inscription : Décembre 2009
    Messages : 33
    Par défaut
    Mon fichier main.h inclut mes classes annexes en .cpp

    A vrai dire j'ai trouvé aucune explication sur comment faire tout ça rigoureusement, donc je fais ça vraiment au pif

  10. #10
    Membre émérite
    Inscrit en
    Juillet 2005
    Messages
    512
    Détails du profil
    Informations forums :
    Inscription : Juillet 2005
    Messages : 512
    Par défaut
    Mon fichier main.h inclut mes classes annexes en .cpp
    Je ne comprend pas trop ta phrase !
    Mais un main.h ce n'est pas utile et on ne fait jamais d'include de cpp.

  11. #11
    Membre averti
    Inscrit en
    Décembre 2009
    Messages
    33
    Détails du profil
    Informations forums :
    Inscription : Décembre 2009
    Messages : 33
    Par défaut
    Je pensais (à tord apparement) que je devais inclure les .cpp pour que le compilo puisse savoir ce que sont les classes que j'utilise, les .h suffisent donc?

  12. #12
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Bon j'ai pas le courage d'expliquer toute la chaine de compilation, la différence entre le compilateur et le linkeur, les .obj etc.

    Pour t'éviter des erreurs de link casse-têtes lezebulon, je te conseille de bien suivre les deux points suivants :

    1) N'inclue jamais de .cpp, toujours des .h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    #include "config.cpp" // NON
    2) Les .h sont pour les déclarations, les .cpp pour les définitions :

    déclaration dans config.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void load_image(); // parfois appelé "signature" ou "prototype"
    définition dans config.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void load_image()
    {
      // corps de la fonction 
    }
    C'est un poil simplificateur, car il y a certains cas où les deux points ci-dessus ne sont plus vrais (par exemple avec les templates) mais pour un débutant ces deux conseils devraient suffire dans la plupart des cas.

  13. #13
    Expert confirmé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 487
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 487
    Par défaut
    Une remarque juste pour montrer que les "ifndef...endif" n'interviennent pas comme certains semblent le penser.

    Dans un projet C++, chaque fichier .cpp est envoyé au préprocesseur qui génère un fichier .s qui contient le code cpp mais en remplaçant les "#include" par leur contenue des fichiers et en remplaçant les MACRO par leur valeur. C'est donc au préprocesseur que s'adresse les "ifndef...endif" pour ne pas avoir des remplacements qui partent en boucle.
    Il y a donc un fichier .s par .cpp.

    Le compilateur prend chaque .s pour générer un .obj pour chacun.

    L'éditeur de lien prend tous les .obj pour construire l'exécutable.

    En ayant une définition de fonction dans un .h, il y aura la définition de cette fonction dans chacun des .s produits à partir d’un .cpp qui inclut, directement ou indirectement, le .h.
    Il y aura donc le code machine de cette fonction dans chacun des .obj correspondant aux .s contenant la définition de la fonction.

    L'éditeur de lien se retrouve donc avec plusieurs implémentations de la fonction et il ne peut choisir.

    Relisez les remarques précédentes sur la non inclusion des .cpp et autre à la lumière de cet exposé très parcellaire de la mécanique de génération et vous verrez qu'elles en découlent toutes.

  14. #14
    Membre actif Avatar de Chessmaster1966
    Inscrit en
    Juillet 2010
    Messages
    63
    Détails du profil
    Informations forums :
    Inscription : Juillet 2010
    Messages : 63
    Par défaut
    Je confirme ce que dit bacelar.


    Voici le processus du code source à l'exécutable :

    CodeSource -> Compilation->FichiersObjets->Lieur->FichierExe

    La phase de compilation :
    Exécute le préprocesseur pour traiter les directives de compilation,
    Puis analyse la syntaxe et si tout est Ok alors il génère les fichiers
    objets (un par fichier "cpp").
    La phase du liage :
    Le liage permet de lier entre-eux tous les fichiers objets ("obj").
    Donc chaque fichier est analysé et les liens sont faits. Si
    tout est Ok il générre le fichier exécutable ("exe").
    (Pour ton cas le lieur constate qu'il y a redéfinition et il n'arrive
    pas à choisir qu'elle version utiliser et il te le signale par ce message.)

    En conclusion regarde dans le fichier "main.cpp" car elle est là la redéfinition puisqu'elle existe déjà dans un des fichiers "cpp" de ton projet !

  15. #15
    Expert confirmé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 487
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 487
    Par défaut
    Si vous relisez bien mon post, vous verrez qu'il est possible d'avoir une redéfinition de la fonction, même si elle n'apparaît qu'une fois dans les fichiers sources.

    Il suffit que l'implémentation de la fonction soit dans un fichier d'en-tête (.h) et que deux fichiers (.cpp) différents fasse l'inclusion, directement ou indirectement, du fichier d'en-tête contenant l’implémentation de la fonction.

    A la fin du pre-processing, le code du la fonction (l'implémentation) apparaîtra dans les 2 fichiers .s générés. Elle sera donc dans les 2 fichiers objet (.obj) générés par le compilateur.

    Et c'est le linker qui dit que vous déconnez, car il voit 2 fonctions avec le même nom dans les fichiers .obj.

    Que le code généré soit le même ne change rien. Et encore, comme les options de compilations peuvent changer d'un .cpp à un autre, donc d'un .s à un autre, le résultat en code machine de la fonction peut être différent dans chacun des .obj.

  16. #16
    Membre actif Avatar de Chessmaster1966
    Inscrit en
    Juillet 2010
    Messages
    63
    Détails du profil
    Informations forums :
    Inscription : Juillet 2010
    Messages : 63
    Par défaut
    Un petit rappel sur l'usage des fichiers d'entête.

    Dans un fichier d'entête (".h") on y met que les déclarations de
    constantes, fonctions, classes, etc...

    Dans un fichier de code source (".cpp") on y mettra uniquement la définition des fonctions, etc... de façon à cacher toute implémentation et donc à préserver la propriété intellectuelle.

    Pour les débutants donc une bonne pratique s'impose exemple :
    test.cpp(définitions) et son fichier d'entête(déclarations) test.h

  17. #17
    Expert confirmé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 487
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 487
    Par défaut
    préserver la propriété intellectuelle
    <HS>
    J'espère que tu ne crois pas à cela. La génération de code n'a jamais été de l'offuscation de code ni du cryptage d'algorithme.
    Un simple décompileur et la protection s'évanoui.
    </HS>

  18. #18
    Membre averti
    Inscrit en
    Décembre 2009
    Messages
    33
    Détails du profil
    Informations forums :
    Inscription : Décembre 2009
    Messages : 33
    Par défaut
    Merci j'ai résolu ça en faisant comme indiqué c'est-à-dire ne pas mettre le corps de ma fonction dans mon .h

  19. #19
    Membre averti
    Inscrit en
    Décembre 2009
    Messages
    33
    Détails du profil
    Informations forums :
    Inscription : Décembre 2009
    Messages : 33
    Par défaut
    Bonjour,
    j'ai (encore) une erreur de linkage que j'arrive pas à déterminer, cette fois-ci c'est le problème inverse :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ain.obj : error LNK2019: symbole externe non résolu "public: __thiscall Sprite::Sprite(class SpriteSet *,class std::map<int,class std::vector<int,class std::allocator<int> >,struct std::less<int>,class std::allocator<struct std::pair<int const ,class std::vector<int,class std::allocator<int> > > > >)" (??0Sprite@@QAE@PAVSpriteSet@@V?$map@HV?$vector@HV?$allocator@H@std@@@std@@U?$less@H@2@V?$allocator@U?$pair@$$CBHV?$vector@HV?$allocator@H@std@@@std@@@std@@@2@@std@@@Z) référencé dans la fonction _SDL_main
    Voici le bout de code en question :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
        SpriteSet ss1("charset.png", 3, 2, false);
     
        std::vector<int> test;
        test.push_back(1);
        test.push_back(2);
     
        std::map<int,  std::vector< int > > map1;
     
        map1.insert(make_pair(1, test));
     
        Sprite s1(&ss1, map1);
    qui donc se compile bien en .obj

    A noter que je ne peux pas non plus écrire :
    Sprite s1;
    dans mon code sans créér une erreur de lien, même si le fichier se compile...

    Une idée?

    Mon constructeur est défini dans le .h comme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Sprite(SpriteSet * spriteset, std::map<int,  std::vector< int > > sprite_surfaces_index);

  20. #20
    Membre averti
    Inscrit en
    Décembre 2009
    Messages
    33
    Détails du profil
    Informations forums :
    Inscription : Décembre 2009
    Messages : 33
    Par défaut
    Résolu : en fait mon Sprite.ccp était pas compillé.... je me demande pourquoi vu qu'il était mis dans les fichiers sources comme le reste de mes .cpp

Discussions similaires

  1. Code::Blocks sous Linux problème de linkage
    Par Invité dans le forum Code::Blocks
    Réponses: 3
    Dernier message: 22/03/2006, 16h54
  2. [Code::Blocks] Problème de linkage: "msvcrt.lib"
    Par skhay dans le forum Code::Blocks
    Réponses: 8
    Dernier message: 14/03/2006, 19h39
  3. Plusieurs fichiers => Problème de Linkage
    Par loic911 dans le forum C++
    Réponses: 6
    Dernier message: 01/03/2006, 00h11
  4. Problème de linkage avec la librairie DevIl
    Par Drannor dans le forum DevIL
    Réponses: 1
    Dernier message: 18/01/2006, 23h05
  5. Problème de linkage
    Par lvdnono dans le forum Windows
    Réponses: 4
    Dernier message: 15/06/2004, 12h32

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