OVH Cloud OVH Cloud

Forward declaration dans 2 libs

6 réponses
Avatar
Cohiba
Bonjour!
J'ai la situation suivante:
Dans une lib LIB_A, je declare la classe A.
Dans une lib LIB_B, je declare la classe B.

Or A contient un pointeur _b de type B, declare comme suit:
LIB_B::B *_b;
Et B contient un pointeur _a de type A, declare comme suit:
LIB_A::A *_a;

A-t-on le droit de faire ca? J'ai tente de le faire en utilisant la
forward declaration dans les deux classes A et B (class LIB_B::B; dans
A, resp. class LIB_A::A; dans B), mais deja a la compilation les
problemes commencent... Par ex., LIB_B::B n'est pas reconnu lors de la
compilation de la lib A.

Merci de votre aide!

Cohiba.

6 réponses

Avatar
Ivan Vecerina
"Cohiba" wrote in message
news:421d8a45$
Bonjour!
J'ai la situation suivante:
Dans une lib LIB_A, je declare la classe A.
Dans une lib LIB_B, je declare la classe B.

Or A contient un pointeur _b de type B, declare comme suit:
LIB_B::B *_b;
Et B contient un pointeur _a de type A, declare comme suit:
LIB_A::A *_a;

A-t-on le droit de faire ca? J'ai tente de le faire en utilisant la
forward declaration dans les deux classes A et B (class LIB_B::B; dans A,
resp. class LIB_A::A; dans B), mais deja a la compilation les problemes
commencent... Par ex., LIB_B::B n'est pas reconnu lors de la compilation
de la lib A.


Cela est possible si les librairies sont représentées par des
namespaces plutôt que des classes.
Les classes A et B peuvent alors être (pré)déclarées:
namespace LIB_A { class A; }
namespace LIB_B { class B; }

Par contre, il n'est pas possible de faire une prédéclaration
d'une classe interne à une autre classe non-encore définie:
class LIB_A; // ok, simple pré-déclaration
class LIB_A::A; //ERREUR, et pas d'alternative valide.


Salutations,
Ivan
--
http://ivan.vecerina.com/contact/?subject=NG_POST <- email contact form
Brainbench MVP for C++ <> http://www.brainbench.com

Avatar
kanze
Cohiba wrote:

J'ai la situation suivante:
Dans une lib LIB_A, je declare la classe A.
Dans une lib LIB_B, je declare la classe B.

Or A contient un pointeur _b de type B, declare comme suit:
LIB_B::B *_b;
Et B contient un pointeur _a de type A, declare comme suit:
LIB_A::A *_a;

A-t-on le droit de faire ca?


Attention : le langage C++ ne connaît pas la notion de
bibliothèque. Il accepte bien qu'on cherche des bibliothèques
pour résoudre les noms externes non résolus, mais le langage
même laisse très ouvert ce que l'implémentation entend par
« bibliothèque », et ce qu'il faut faire pour en créer une. Dans
la plupart des implémentations, où au moins, dans toutes que
j'ai vues, une bibliothèque n'est autre chose qu'une collection
des fichiers objets. Et il n'y a pas d'indication au niveau des
sources ce qui est bibliothèque et ce qui ne l'est pas, et
encore moins de quelle bibliothèque un symbole vient.

Ayant dit ceci, une convention possible, c'est d'utiliser un
espace référentiel par bibliothèque. Dans ce cas-là, les
déclarations externes auraient la forme :

namespace LIB_B {
class B {
// ...
} ;
} ;

namespace LIB_A {
LIB_B::B* _b ;
}

Typiquement, c'est déclarations se trouveront dans un ou
plusieurs fichiers d'en-tête.

Si on a besoin des déclarations prévisionnelles (à cause des
cycles dans les références), on pourrait faire quelque chose du
genre :

namespace LIB_B {
class B ;
}

Ici, personnellement, je pencherais aussi vers des en-têtes, sur
le modèle de <iosfwd>, plutôt que des déclarations explicites
dans des douzaines de fichiers différents. Mais les pratiques à
cet égard varient. (À vrai dire, les pratiques vis-à-vis des
espaces référentiels en général varient, et je n'ai vu qu'une
seule application jusqu'ici qui s'en servais.)

Enfin, une rémarque plus générale : Je me méfie un peu d'une
référence cyclique entre bibliothèques. Ça suggère fort une
erreur de conception. (N'oublie pas qu'une fois la bibliothèque
LIB_A traitée, la plupart des implémentations ne reviendront
plus en arrière le rétraiter quand elles traite la bibliothèque
LIB_B, à moins que tu ne le démandes explicitement.) Peut-être
ta découpe en bibliothèques est trop fine, et que les deux
classes seraient mieux dans la même bibliothèque. (AMHA, des
références cycliques ne sont acceptables que si les classes sont
très apparentées, qu'elles travaillent ensembles. Ce qui veut
dire qu'elles sont membres de la même bibliothèque.)

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
Falk Tannhäuser
wrote:
Enfin, une rémarque plus générale : Je me méfie un peu d'une
référence cyclique entre bibliothèques. Ça suggère fort une
erreur de conception. (N'oublie pas qu'une fois la bibliothèque
LIB_A traitée, la plupart des implémentations ne reviendront
plus en arrière le rétraiter quand elles traite la bibliothèque
LIB_B, à moins que tu ne le démandes explicitement.) Peut-être
ta découpe en bibliothèques est trop fine, et que les deux
classes seraient mieux dans la même bibliothèque. (AMHA, des
références cycliques ne sont acceptables que si les classes sont
très apparentées, qu'elles travaillent ensembles. Ce qui veut
dire qu'elles sont membres de la même bibliothèque.)


Dans le cas contraire (CAD si on veut garder les deux
bibliothèques séparées) il faudrait casser la dépendance
circulaire entre les deux. Une manière possible pour y
arriver en C++ serait d'introduire une classe de base
abstraite dont les deux classes LIB_A::A et LIB_B::B
dépendent, au lieu de dépendre l'une de l'autre directement.

Exemple: LIB_A contient une classe 'Server' et LIB_B
une classe 'Client', les clients doivent envoyer des
requêtes à un serveur et celui-ci doit renvoyer une
réponse. Ceci nécessite une connaissance mutuelle entre
serveur et client. Une implémentation possible pourrait
ressembler à ceci :
________________ fichier LibA.h ______________________
namespace LIB_A
{

class AbstractClient
{
public:
virtual void Response(param ...) = 0;
virtuel ~AbstractClient();
};

class Server
{
public:
void Request(AbstractClient& client, param ...);
// Enclenche un traitement quelconque, puis appelle
// client.Response(...);
};

} // namespace LIB_A

________________ fichier LibB.h ______________________
#include "LibA.h"
namespace LIB_B
{

class Client : public LIB_A::AbstractClient
{
public:
void FaisQuelqueChose(Server& srv)
{
...
srv.Request(...);
}

virtual void Response(...);
};

} // namespace LIB_B
______________________________________________________

Ainsi, LIB_B dépend toujours de LIB_A (nécessité de
#include "LibA.h") mais plus l'inverse. Une autre avantage
est qu'on peut facilement avoir plusieurs classes de
clients.

Bien sûr, il y à d'autres possibilités d'éviter des
dépendances circulaires, par exemple en utilisant des
templates...

Falk

Avatar
Jean-Marc Bourguet
writes:

Peut-être ta découpe en bibliothèques est trop fine, et que les deux
classes seraient mieux dans la même bibliothèque.


Ou pas assez: les deux classes devraient peut-etre etre ensemble dans
une troisieme bibliotheque.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Siman
Cohiba wrote:

Bonjour!
J'ai la situation suivante:
Dans une lib LIB_A, je declare la classe A.
Dans une lib LIB_B, je declare la classe B.

Or A contient un pointeur _b de type B, declare comme suit:
LIB_B::B *_b;
Et B contient un pointeur _a de type A, declare comme suit:
LIB_A::A *_a;

A-t-on le droit de faire ca? J'ai tente de le faire en utilisant la
forward declaration dans les deux classes A et B (class LIB_B::B; dans
A, resp. class LIB_A::A; dans B), mais deja a la compilation les
problemes commencent... Par ex., LIB_B::B n'est pas reconnu lors de la
compilation de la lib A.

Merci de votre aide!

Cohiba.


Salut,

Je débute en C++ et si j'ai bien compris on m'a donné l'astuce suivante pour
un problème similaire :

--- a.h -----

class b {
//fausse classe B
//maintenant le compilo connait la class B.
};

class a {

public :
b* toto;

};

--- b.h ------

class b {
//la bonne classe B
};


Pour que cette astuce marche il faut placer la fausse classe dans le
premier .h qui sera lu par le compilo.

Voilant en ésperant ne pas avoir dit de bétise.

--
Cyril

Avatar
Jean-Marc Bourguet
J'ai pas trop le temps mais...

Siman writes:

Je débute en C++ et si j'ai bien compris on m'a donné l'astuce
suivante pour un problème similaire :

--- a.h -----

class b {
//fausse classe B
//maintenant le compilo connait la class B.
};


class b;

Definir une meme classe de differentes manieres n'est pas correct.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org