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 :

inclusion cyclique et compilation


Sujet :

C++

Vue hybride

Message pr�c�dent Message pr�c�dent   Message suivant Message suivant
  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Ao�t 2009
    Messages
    6
    D�tails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Ao�t 2009
    Messages : 6
    Par d�faut inclusion cyclique et compilation
    Bonjour,

    malgr� mes recherches je ne suis pas certain d'avoir bien compris les raisons qui font que parfois lors d'inclusions cycliques il y a erreur de compilation.

    J'ai fait quelque petits tests:

    Soit la classe A :

    A.h:

    #ifndef A_H_
    #define A_H_

    #include "B.h"

    class A {

    public:
    void a(B b);

    };

    #endif
    et la classe B :

    B.h:
    #ifndef B_H_
    #define B_H_

    #include "A.h"

    class B {

    public:
    void b();

    };

    #endif

    Ca ne compile donc pas avec l'erreur suivante:

    In file included from B.h:11,
    from B.cpp:9:
    A.h:16: error: �B� has not been declared
    Ce que j'interpr�te comme �a:

    Dans B.cpp, avec la directive d'inclusion le pr�processeur copie-colle B.h et le parse :

    or B.h inclut A.h donc le pr�processeur copie-colle aussi A.h et le parse:

    il rencontre le symbole "B" dans la fonction "void a(B b)", le compilo veut connaitre l'adresse m�moire de ce symbole (via la table des symboles) mais il ne peut pas car la classe B n'a pas encore �t� compil�e.

    Est ce que c'est vraiment ce qu'il se passe ?

    Si oui alors pourquoi apr�s:
    - avoir enlev� le symbole B de ma classe A pour avoir simplement "void a()".
    - et utiliser le symbole B dans ma classe B , par exemple avec une fonction "void b(B b);"

    pourquoi est ce que �a compile ? ca ne devrait pas puisqu'il ne connait toujours pas l'adresse du symbole B...??...

  2. #2
    Membre Expert
    Avatar de poukill
    Profil pro
    Inscrit en
    F�vrier 2006
    Messages
    2 155
    D�tails du profil
    Informations personnelles :
    �ge : 41
    Localisation : France

    Informations forums :
    Inscription : F�vrier 2006
    Messages : 2 155

  3. #3
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Ao�t 2009
    Messages
    6
    D�tails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Ao�t 2009
    Messages : 6
    Par d�faut
    merci chef mais j'avais bien compris comment r�soudre le pb .

    Ce qui m'int�resse c'est la "sauce interne"du compilo pour ne pas appliquer b�tement les solutions ^^ .

    Par exemple:

    on va simplement d�clarer celle-ci pour indiquer au compilateur qu'elle existe. Cela marche car tant qu'on n'utilise qu'un pointeur ou une r�f�rence, le compilateur n'a pas besoin de conna�tre en d�tail le contenu de la classe. Il a juste besoin de savoir qu'elle existe. Par contre au moment d'utiliser celle-ci (appel d'une fonction membre par exemple) il faudra bien avoir inclus son en-t�te, mais ce sera fait dans le .cpp et non plus dans le .h,
    Ce sont certainement de bonnes recettes mais concr�tement ca signifie quoi au niveau de l'analyse lexicale et syntaxique du compilo ? de la table des symboles ?

  4. #4
    Expert �minent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    �ge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activit� : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par d�faut
    Salut, et bienvenue sur le forum.

    La "sauce interne" du compilateur est simple: il parcourt ton code de haut en bas, et de gauche � droite, exactement comme la mani�re dont tu parcours un roman qui te plait

    Et, tout comme tu ignore ce qui se passe � la page 12 quand tu n'a lu que les dix premi�res, le compilateur ne connait que ce qu'il a d�j� rencontr� (il ne connait que ce qui a �t� d�clar� ou d�fini dans les lignes pr�c�dentes).

    Tant que le compilateur n'a pas besoin de savoir de quoi est compos�e une classe, il peut donc se contenter de... savoir que la classe en question existe.

    Et, pour lui dire que la classe existe, il "suffit" de... la d�clarer (c'est ce que l'on appelle la "d�claration anticip�e") sous la forme de
    Evidemment, une fois d�s qu'il est question pour le compilateur de savoir l'espace que la classe prend en m�moire ou, de mani�re g�n�rale, d'acc�der au contenu de la classe, le seul fait de savoir qu'elle existe ne suffit plus: il doit... savoir de quoi elle est compos�e.

    Il doit donc � ce moment l� disposer... de la d�finition de la classe.

    Maintenant, int�ressons nous un peu � ce que fait le compilateur.

    Pour que tu comprenne le principe, il faut savoir qu'il y a trois grandes �tapes � la compilation:
    1. La gestion des directives pr�processeur
    2. La g�n�ration d'un code binaire
    3. l'�dition de liens
    Tout le probl�me des inclusions cycliques trouve son origine dans la premi�re �tape.

    Pour que nous parlions la m�me langue, nous pouvons dire que toute ligne qui commence par un "#" est une instruction pr�processeur, et tout se joue sur les directive #include.

    Lorsque le pr�processeur rencontre une directive #include, recopie lit�ralement l'ensemble du fichier dont le nom est donn� en lieu et place de la dite directive.

    Seulement, cette directive s'applique de mani�re r�cursive (si a.hpp inclus b.hpp qui inclus c.hpp qui inclus d.hpp, nous retrouverons dans a.hpp le contenu de b.hpp, de c.hpp et de d.hpp )

    Si nous avons deux fichiers d'en-t�te (faisons simple: a.hpp et b.hpp) qui ressemble �
    a.hpp
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
    6
    7
    8
    #ifndef A_HPP
    #define A_HPP
    #include "b.hpp"
    class A
    {
        /*...*/
    };
    #endif // A_HPP
    et � b.hpp
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
    6
    7
    8
    #ifndef B_HPP
    #define B_HPP
    #include "a.hpp"
    class b
    {
        /*...*/
    };
    #endif // B_HPP
    lorsque le pr�processeur va travailler, nous aurons quelque chose dont le r�sultat ressemblera fort �
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    /* début de a.hpp */
    #ifndef A_HPP
    #define A_HPP
    /* la directive #include b.hpp est remplacée par le contenu du fichier en 
     * question
     */
    #ifndef B_HPP
    #define B_HPP
    /* ici vient normalement... la directive #include a.hpp qui est donc...
     * remplacée par le contenu du fichier
     */
    #ifndef A_HPP
    #define A_HPP
    /* ici vient normalement ...
     * bref, on peut continuer 106 ans de la sorte :P
     */
    Ce n'est qu'apr�s cette �tape de remplacement des directives include (qui finit en cercle vicieux) qu'une deuxi�me �tape va g�rer les gardes anti inclusions et faire en sorte... de supprimer les parties correspondant aux symboles d�j� d�fini.

    Mais, encore faut il arriver � sortir de cette inclusion cyclique, et il n'y a, malheureusement, aucun moyen d'y arriver

    C'est la raison pour laquelle, si deux classes font r�f�rence l'une � l'autre, il faut trouver un "autre moyen" de travailler.

    Cet autre moyen est, finalement, relativement simple:
    nous d�clarons anticipativement la classe A dans b.hpp et la classe B dans a.hpp
    nous n'utilisons que des pointeurs ou des r�f�rences vers la classe B dans a.hpp et vers la classe A dans b.hpp
    nous incluons le fichier d'en-t�te (a.hpp et b.hpp) dans... le fichier d'impl�mentation (respectivement b.cpp et a.cpp), c'est � dire, "le plus tard possible" par rapport au moment o�... le compilateur doit disposer du contenu des deux classes.

    Au final, les fichiers ressemblent donc �
    fichier a.hpp
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #ifndef A_HPP
    #define A_HPP
    class B; // juste la déclaration, cela suffit ici vu qu'il faut juste que le 
             // compilateur sache qu'elle existe
    class A
    {
        public:
             void foo();
        private:
            B * b_; // la taille utilisée en mémoire par n'importe quel pointeur est
                    // connue, même si elle dépend du système
    };
    #endif // A_HPP
    fichier b.hpp
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #ifndef B_HPP
    #define B_HPP
    class A; // juste la déclaration, cela suffit ici vu qu'il faut juste que le 
             // compilateur sache qu'elle existe
    class B
    {
        public:
             void bar();
        private:
            A * a_; // la taille utilisée en mémoire par n'importe quel pointeur est
                    // connue, même si elle dépend du système
    };
    #endif // B_HPP
    fichier a.cpp
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    /* il faut que le compilateur connaisse le contenu de la classe A,
     * vu que l'on définit... les fonctions de A ;)
     */
    #include "a.hpp"
    /* et il doit connaitre le contenu de la classe B (pour pouvoir invoquer bar
     * depuis le pointeur sur B qui existe dans A)
     */
    #include "b.hpp"
    void A::foo()
    {
        b_->bar();
    }
    fichier b.cpp
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /* il faut que le compilateur connaisse le contenu de la classe B,
     * vu que l'on définit... les fonctions de B ;)
     */
    #include "b.hpp"
    /* et il doit connaitre le contenu de la classe A (pour pouvoir invoquer foo
     * depuis le pointeur sur A qui existe dans B)
     *
     * en pratique, il n'y aura sans doute pas une telle récursivité, ou il y
     * aura des tests permettant d'y mettre fin :D
     */
    #include "a.hpp"
    void b::bar()
    {
        a_->foo();
    }
    A m�diter: La solution la plus simple est toujours la moins compliqu�e
    Ce qui se con�oit bien s'�nonce clairement, et les mots pour le dire vous viennent ais�ment. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 f�vrier 2014
    mon tout nouveau blog

  5. #5
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Ao�t 2009
    Messages
    6
    D�tails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Ao�t 2009
    Messages : 6
    Par d�faut
    merci pour la longue r�ponse .

    Cependant je m'excuse mais ce n'est pas tr�s clair. Je crois que j'ai compris le syst�me des #ifndef... mais mon probl�me se pose plus sur la r�solution des symboles par le compilateur

    Par exemple la phrase suivante:
    // la taille utilis�e en m�moire par n'importe quel pointeur est connue, m�me si elle d�pend du syst�me
    Je suis enti�rement d'accord avec �a, mais alors pourquoi par exemple le code suivant va compiler ?

    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #ifndef B_H_
    #define B_H_
     
     
    class B {
     
    public:
    	void f(B b);  //à ce stade on ne sait pas non plus la taille 
    //que va prendre B en mémoire puisque la classe B n'a pas fini de compiler non ?
    };
     
    #endif

  6. #6
    Expert �minent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    �ge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activit� : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par d�faut
    Citation Envoy� par qertbu65 Voir le message
    merci pour la longue r�ponse .

    Cependant je m'excuse mais ce n'est pas tr�s clair. Je crois que j'ai compris le syst�me des #ifndef... mais mon probl�me se pose plus sur la r�solution des symboles par le compilateur

    Par exemple la phrase suivante:


    Je suis enti�rement d'accord avec �a, mais alors pourquoi par exemple le code suivant va compiler ?

    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #ifndef B_H_
    #define B_H_
     
     
    class B {
     
    public:
    	void f(B b);  //à ce stade on ne sait pas non plus la taille 
    //que va prendre B en mémoire puisque la classe B n'a pas fini de compiler non ?
    };
     
    #endif
    Si tu sais pertinemment la taille que va prendre la classe, parce que tu te trouve dans le bloc de d�finition de la classe.

    Par contre, tu auras un probl�me avec un code proche de
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    class B;
    void foo(B b) // le problème intervient ici :D
    {
        b.bar();
    }
    class B
    {
        public:
            void bar(){cout<<"B::bar()"<<endl;}
    };
    parce que le compilateur sait qu'il existe un type B quand il croise foo(B b), mais il ne dispose pas du type complet. (error 'B' has incomplete type)
    A m�diter: La solution la plus simple est toujours la moins compliqu�e
    Ce qui se con�oit bien s'�nonce clairement, et les mots pour le dire vous viennent ais�ment. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 f�vrier 2014
    mon tout nouveau blog

+ R�pondre � la discussion
Cette discussion est r�solue.

Discussions similaires

  1. Typedef et inclusion cyclique
    Par Johjo dans le forum Langage
    R�ponses: 7
    Dernier message: 22/10/2010, 14h33
  2. d'inclusion cyclique et makefile
    Par vincent.mbg dans le forum C
    R�ponses: 4
    Dernier message: 14/06/2010, 18h05
  3. probleme inclusion cyclique avec namespace
    Par befalimpertinent dans le forum C++
    R�ponses: 4
    Dernier message: 27/05/2010, 09h52
  4. probl�me inclusion cyclique
    Par marion5515 dans le forum D�buter
    R�ponses: 7
    Dernier message: 20/05/2009, 13h53
  5. Inclusion cyclique: mauvaise analyse?
    Par zabibof dans le forum C++
    R�ponses: 4
    Dernier message: 12/09/2007, 16h36

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