OVH Cloud OVH Cloud

Object contenant une liste d'objet

6 réponses
Avatar
Fabien SK
Bonjour,

Je veux faire une classe représentant un arbre d'objets "XmlNode". Je
voulais dans ma classe "XmlNode" avoir une liste d'objets de ce type.

class XmlNode
{
std::list<XmlNode> m_NodeList;
};

Sauf que VC6 me jette, car dans la classe "std::list" il définit une
classe interne:

struct _Node {
_Nodeptr _Next, _Prev;
_Ty _Value;
};

"_Ty" est ici ma classe "XmlNode". Le compilateur n'est pas content:

'_Value' uses undefined class 'XmlNode'

g++ 3.3.1 n'a pas ce problème (avec sa propre implémentation de la STL),
mais je dois compiler avec VC6. Je crois que je suis fichu, je vais
devoir mettre comme membre de ma classe un pointeur sur liste :-(
Vous croyez que c'est de la faute à un compilateur obsolète ?

Merci de votre attention
Fabien

6 réponses

Avatar
Benoit Rousseau
Fabien SK wrote:
Bonjour,

Je veux faire une classe représentant un arbre d'objets "XmlNode". Je
voulais dans ma classe "XmlNode" avoir une liste d'objets de ce type.

class XmlNode
{
std::list<XmlNode> m_NodeList;
};

Sauf que VC6 me jette, car dans la classe "std::list" il définit une
classe interne:

struct _Node {
_Nodeptr _Next, _Prev;
_Ty _Value;
};

"_Ty" est ici ma classe "XmlNode". Le compilateur n'est pas content:

'_Value' uses undefined class 'XmlNode'

g++ 3.3.1 n'a pas ce problème (avec sa propre implémentation de la STL),
mais je dois compiler avec VC6. Je crois que je suis fichu, je vais
devoir mettre comme membre de ma classe un pointeur sur liste :-(
Vous croyez que c'est de la faute à un compilateur obsolète ?

Merci de votre attention
Fabien



Est ce que ce n'est pas le même problème que l'on a lorsqu'on veut faire
class XmlNode {
XmlNode Value; //Definition récursive non autorisée si non ptr
};

Pourquoi ne pas utiliser un composite pattern plus classique que celui
la ? Puisque XmlNode peut contenir des XmlNode ou un "XmlValue" (une
chaine?).

Un element est défini par
element ::= EmptyElemTag
| STag content ETag

EmptyElemTag ::= '<' Name (S Attribute)* S? '/>'
STag et ETag ::= '<...>'
et
content ::= (element | CharData | Reference | CDSect | PI | Comment)*
content correspond à ta liste et peut contenir element ou CharData (ou
le reste)

ou bien utilise std::list<XmlNode * > ?

--
--------------------------------------------
Benoît Rousseau : roussebe at spray dot se
Jouez en programmant : http://realtimebattle.sourceforge.net/

Avatar
Fabien SK
Benoit Rousseau wrote:

Fabien SK wrote:

Bonjour,

Je veux faire une classe représentant un arbre d'objets "XmlNode". Je
voulais dans ma classe "XmlNode" avoir une liste d'objets de ce type.

class XmlNode
{
std::list<XmlNode> m_NodeList;
};

Sauf que VC6 me jette, car dans la classe "std::list" il définit une
classe interne:

struct _Node {
_Nodeptr _Next, _Prev;
_Ty _Value;
};

"_Ty" est ici ma classe "XmlNode". Le compilateur n'est pas content:

'_Value' uses undefined class 'XmlNode'

g++ 3.3.1 n'a pas ce problème (avec sa propre implémentation de la
STL), mais je dois compiler avec VC6. Je crois que je suis fichu, je
vais devoir mettre comme membre de ma classe un pointeur sur liste :-(
Vous croyez que c'est de la faute à un compilateur obsolète ?

Merci de votre attention
Fabien



Est ce que ce n'est pas le même problème que l'on a lorsqu'on veut faire
class XmlNode {
XmlNode Value; //Definition récursive non autorisée si non ptr
};


Non, si j'écris une classe de liste chaînée, quand je vais instancier un
objet "liste<Object>" je n'ai pas besoin de connaitre la taille d'un
"Objet" (enfin tel que je concevrais une implémentation).
D'ailleurs, la classe "std::list" de cette implémentation a comme membre
un "_Node*" et pas un "_Node".

Pourquoi ne pas utiliser un composite pattern plus classique que celui
la ? Puisque XmlNode peut contenir des XmlNode ou un "XmlValue" (une
chaine?).
Un element est défini par
element ::= EmptyElemTag
| STag content ETag
EmptyElemTag ::= '<' Name (S Attribute)* S? '/>'
STag et ETag ::= '<...>'
et
content ::= (element | CharData | Reference | CDSect | PI | Comment)*
content correspond à ta liste et peut contenir element ou CharData
(ou > le reste)


Ma classe "XmlNode" contient les informations sur le traitement d'un
noeud, je ne découpe pas le XML moi-même (merci MS-XML; et ce n'est pas
le problème ici d'ailleurs). En fait, tu me dis que je dois faire une
liste de "Content*" (qui me permet d'avoir des classes filles à
"Content"), mais ce n'est pas mon besoin. Ca ramène à ce que tu me
proposes ci-dessous.

ou bien utilise std::list<XmlNode * > ?
C'est finalement ce que j'ai fait. Mais c'est rajouter du code pour la

libération des noeuds quand on peut l'éviter ?

Merci de ta réponse


Avatar
Bertrand Motuelle
"Fabien SK" <fabsk+ schrieb im Newsbeitrag
news:3fb4e623$0$249$

class XmlNode
{
std::list<XmlNode> m_NodeList;
};

Sauf que VC6 me jette, car dans la classe "std::list" il définit une
classe interne:

g++ 3.3.1 n'a pas ce problème (avec sa propre implémentation de la STL),
mais je dois compiler avec VC6. Je crois que je suis fichu, je vais
devoir mettre comme membre de ma classe un pointeur sur liste :-(
Vous croyez que c'est de la faute à un compilateur obsolète ?



Selon la norme (17.4.3.6), instancier un template de la librarie standard
est un comportement indéfini.
Dans ton cas, pour gcc, le comportement indéfini est de faire les choses
correctement...

Bertrand.

Avatar
kanze
"Bertrand Motuelle" wrote in message
news:<bp3gql$kn8$...
"Fabien SK" <fabsk+ schrieb im Newsbeitrag
news:3fb4e623$0$249$

class XmlNode
{
std::list<XmlNode> m_NodeList;
};

Sauf que VC6 me jette, car dans la classe "std::list" il définit une
classe interne:

g++ 3.3.1 n'a pas ce problème (avec sa propre implémentation de la
STL), mais je dois compiler avec VC6. Je crois que je suis fichu, je
vais devoir mettre comme membre de ma classe un pointeur sur liste
:-( Vous croyez que c'est de la faute à un compilateur obsolète ?


Selon la norme (17.4.3.6), instancier un template de la librarie
standard est un comportement indéfini.


J'espère que non:-).

Mais je suis sûr que ce que tu voulais dire, c'est que l'instantiation
est un comportement indéfini *SI* les paramètres type sont des types
inachevés.

Dans ton cas, pour gcc, le comportement indéfini est de faire les
choses correctement...


Pour quelle définition de correctement ? Quand il y a comportement
indéfini, tous les compilateurs sont corrects, selon la norme.

Idéalement, en mode strict au moins, on amèreait un avertissement chaque
fois qu'il y a comportement indéfini. Dans la pratique, on ne va pas
l'avoir. Mais une erreur ou un avertissement est toujours le
comportement préféré dans de tels cas.

Sauf, évidemment, quand ce n'est pas le cas. Il y a un ou deux cas des
comportements indéfinis selon la norme où en fait, il y a un
comportement standard de facto. Donc, par exemple :

std::vector< char > data ;
int ch = std::cin.get() ;
while ( ch != EOF && isXXX( ch ) ) {
data.push_back( ch ) ; // Comportemment indéfini, peut core
}

L'idiome est trop répandu pour qu'un compilateur l'ignore. Un
compilateur qui réfuserait à le compiler, ou qui génèrerait du code qui
faisait autre chose que ce qu'on s'attend, se moque de ces utilisateurs.

De même :

(avec le compilateur invoqué avec -Dpthreads, -Dwinthreads, -Dnothreads,
ou quelque chose de semblable):

#define PASTE5( a, b, c, d, e ) a ## b ## c ## d ## e
#define DEPENDANT_FILE_NAME( dir, file ) PASTE5( <, dir, /, file, > #)
#include DEPENDANT_FILE_NAME( threadModel, Mutex.hh )

Selon la norme, si le résultat de ## n'est pas un token valid au
préprocesseur, le comportement est indéfini. Mais des choses semblables
à ceci est plus ou moins la façon standard (de facto) pour gerer des
dépendances de l'environement. En fait, ce qu'on s'attend, c'est
seulement que le résultat d'une séquence de ## soit un token valide, et
qu'il le soit dans le contexte où il apparaît. (En général, le résultat
<pthreads/Mutex.hh> n'est pas un token valide. Derrière un #include, en
revanche, si.)

Un compilateur qui réfuserait de le compiler, ou faisait autre chose que
ce à laquelle on s'attend, se moque de ces utilisateurs.

--
James Kanze GABI Software mailto:
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16


Avatar
Bertrand Motuelle
wrote:
"Bertrand Motuelle" wrote in message
news:<bp3gql$kn8$...

Selon la norme (17.4.3.6), instancier un template de la librarie
standard est un comportement indéfini.



J'espère que non:-).


Heureusement que tu suis James :-)

Mais je suis sûr que ce que tu voulais dire, c'est que l'instantiation
est un comportement indéfini *SI* les paramètres type sont des types
inachevés.


Ben oui.
Pour ceux que ca interesse, Austern a ecrit un article sur les
motivations de cette restriction:
http://www.cuj.com/documents/sy86/cujcexp2002austern/


Dans ton cas, pour gcc, le comportement indéfini est de faire les
choses correctement...



Pour quelle définition de correctement ?


Dans le sens ou on l´entend quand on travaille avec des listes chainees.

Quand il y a comportement indéfini, tous les compilateurs sont corrects, selon la norme.
Idéalement, en mode strict au moins, on amèreait un avertissement chaque
fois qu'il y a comportement indéfini. Dans la pratique, on ne va pas
l'avoir. Mais une erreur ou un avertissement est toujours le
comportement préféré dans de tels cas.


Sur ce point la, je ne suis pas sur. Si gcc implemente ses collections
de sorte qu´elles fonctionnent avec des types incomplets, je ne vois pas
de probleme (ok, si c´etait faisable un avertissement en mode strict
pourrait rendre service a l'utilisateur qui n'est pas au courant).
Surtout, il me semble ca peut ensuite donner des idees aux autres vendeurs.

Sauf, évidemment, quand ce n'est pas le cas. Il y a un ou deux cas des
comportements indéfinis selon la norme où en fait, il y a un
comportement standard de facto. Donc, par exemple :

std::vector< char > data ;
int ch = std::cin.get() ;
while ( ch != EOF && isXXX( ch ) ) {
data.push_back( ch ) ; // Comportemment indéfini, peut core
}



L'idiome est trop répandu pour qu'un compilateur l'ignore. Un
compilateur qui réfuserait à le compiler, ou qui génèrerait du code qui
faisait autre chose que ce qu'on s'attend, se moque de ces utilisateurs.

De même :

(avec le compilateur invoqué avec -Dpthreads, -Dwinthreads, -Dnothreads,
ou quelque chose de semblable):

#define PASTE5( a, b, c, d, e ) a ## b ## c ## d ## e
#define DEPENDANT_FILE_NAME( dir, file ) PASTE5( <, dir, /, file, > #)
#include DEPENDANT_FILE_NAME( threadModel, Mutex.hh )

Selon la norme, si le résultat de ## n'est pas un token valid au
préprocesseur, le comportement est indéfini. Mais des choses semblables
à ceci est plus ou moins la façon standard (de facto) pour gerer des
dépendances de l'environement. En fait, ce qu'on s'attend, c'est
seulement que le résultat d'une séquence de ## soit un token valide, et
qu'il le soit dans le contexte où il apparaît. (En général, le résultat
<pthreads/Mutex.hh> n'est pas un token valide. Derrière un #include, en
revanche, si.)

Un compilateur qui réfuserait de le compiler, ou faisait autre chose que
ce à laquelle on s'attend, se moque de ces utilisateurs.


Avec toi, on a pas besoin de moderateurs pour fclc++ :-)


Avatar
kanze
Bertrand Motuelle wrote in message
news:<bpbe51$29mv$...
wrote:
"Bertrand Motuelle" wrote in message
news:<bp3gql$kn8$...



[...]
Dans ton cas, pour gcc, le comportement indéfini est de faire les
choses correctement...


Pour quelle définition de correctement ?


Dans le sens ou on l´entend quand on travaille avec des listes
chainees.


C'est ce que j'avais compris. De l'autre côté, on a un comportement
indéfini, c-à-d un programme erroné dont la norme n'exige pas de
diagnostique. Alors le mieux (le plus correct, si on veut), ne serait-ce
pas un diagnostique ?

Quand il y a comportement indéfini, tous les compilateurs sont
corrects, selon la norme. Idéalement, en mode strict au moins, on
amèreait un avertissement chaque fois qu'il y a comportement
indéfini. Dans la pratique, on ne va pas l'avoir. Mais une erreur ou
un avertissement est toujours le comportement préféré dans de tels
cas.


Sur ce point la, je ne suis pas sur. Si gcc implemente ses collections
de sorte qu´elles fonctionnent avec des types incomplets, je ne vois
pas de probleme (ok, si c´etait faisable un avertissement en mode
strict pourrait rendre service a l'utilisateur qui n'est pas au
courant). Surtout, il me semble ca peut ensuite donner des idees aux
autres vendeurs.


D'une part. De l'autre, ça peut faire croire aux utilisateurs que leur
programme est correct, quand il ne l'est pas.

L'idéal, je suis d'accord avec toi, ça serait de l'accepter comme
extension, et de le rejeter en mode conformant. L'idéal, aussi, ça
serait que dans l'absense des options qui spécifient autrement, je n'ai
pas d'extension. Ou peut-être, dans le cas de g++, si je l'invoque avec
la commande c++, je suis en mode strict, et avec g++, j'ai des
extensions.

Mais il y a bien de raisons pourquoi cet idéal n'est pas possible. Pour
commencer, je travaille sur un système Posix. Alors, quand j'invoque le
compilateur sans options, est-ce qu'il doit être conforme à la norme
Posix, ou à la norme C++ ? Et puis, il y a le problème de l'existant :
j'ai des applications, écrites il y a un certain temps, qui sont
conforme à ce qui était la norme (de facto) alors, ou qui sont conforme
simplement à ce que faisait le compilateur alors. On est bien d'accord,
je crois, que je dois pouvoir compiler ces programmes aujourd'hui aussi,
sans avoir besoin de les modifier ; sans ça, le compilateur n'est pour
ainsi dire sans intérêt dans l'industrie (où il y a bien peu de boîtes
aujourd'hui qui n'ont pas une seule ligne de C++ déjà écrit, et encore
moins qui peut se payer le lux de modifier les programmes qui marchent
juste pour être à jour). Seulement, est-ce que cette mode de
compatibilité doit être le défaut ? J'ai une tendance à dire que non,
qu'il faudrait donc des options spécifiques du compilateur pour
l'invoquer. Mais ça casse au moins les fichiers de make, ce qui implique
une mise à jour d'un « produit » qui marche.

Sans parler des problèmes techniques à l'implémenter.

--
James Kanze GABI Software mailto:
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16