[long][C++ Windows] Probleme de transtypage

Le
Olivier Miakinen
Bonjour,

Je suis en train de faire le portage sur Visual Studio 2005 (ou Visual
Studio 8.0, il me semble que c'est la même chose) d'un immense projet
qui fonctionnait avec Visual C++ 98 (ou Visual C++ 6.0). Je tombe en
particulier sur un bout de code qui me semble assez laid mais qui ne
plantait pas et qui plante maintenant, et j'aimerais savoir ce que je
dois modifier pour qu'il retombe en marche.

Ce code met en ½uvre pas moins de 7 classes différentes, que pour
simplifier je renomme ici A, B, C, D, E, F et G. Les déclarations
sont les suivantes.

class A : public B, C
{ };

class B : public D, public E
{ };

class C
{ };

class D : public F
{ };

class E
{ };

class F : public G
{ };

J'ai tout mis pour être le plus précis possible, mais je suppose que
le problème (que je n'ai pas encore exposé) serait le même avec la
simple déclaration suivante.

class A : public G, C
{ };

Dans A, il y a une méthode A::une_methode qui appelle la fonction
une_fonction en lui passant le paramètre this.

A::une_methode()
{

une_fonction(this);

}

La fonction une_fonction est la suivante.

une_fonction(C *pc)
{

G *pg = dynamic_cast<G *>(pc);

}

**** LE PROBLÈME ****

Le problème est que pc est non nul, mais que pg est nul. Pourtant, le
débugueur m'indique que pc est bien un pointeur vers un objet de type A,
et A hérite bien d'un (et un seul) objet de type G.

Que puis-je faire ? Je précise que j'ai le droit de modifier les
déclarations et définitions des premières classes, en particulier
A, B et C, et aussi de A::une_methode et de une_fonction.

Par exemple, est-ce que déclarer que C hérite aussi de G résoudrait le
problème ? Il faudrait bien sûr qu'il y ait un seul G au final dans A,
mais je ne sais pas comment déclarer cela.

Cordialement,
--
Olivier Miakinen
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Franck Branjonneau
Le #306332
Olivier Miakinen
J'ai tout mis pour être le plus précis possible, mais je suppose que
le problème (que je n'ai pas encore exposé) serait le même avec la
simple déclaration suivante.

class A : public G, C
{ ... };

[ Code équivalent à

A a;
C * pc(&a);
G * pg(dynamic_cast< G * >(pc));

]

**** LE PROBLÈME ****

Le problème est que pc est non nul, mais que pg est nul.


G et C sont-ils polymorphes ?

Pourtant, le débugueur m'indique que pc est bien un pointeur vers un objet
de type A, et A hérite bien d'un (et un seul) objet de type G.

Par exemple, est-ce que déclarer que C hérite aussi de G résoudrait le
problème ? Il faudrait bien sûr qu'il y ait un seul G au final dans A,
mais je ne sais pas comment déclarer cela.


L'héritage en diamant c'est :

class Base { public: virtual ~Base(); };

class Left: public virtual Base {};
class Right: public virtual Base {};

class Derived: public Left, public Right {};

Derived "a" une unique Base.

--
Franck Branjonneau

Olivier Miakinen
Le #306330

J'ai tout mis pour être le plus précis possible, mais je suppose que
le problème (que je n'ai pas encore exposé) serait le même avec la
simple déclaration suivante.

class A : public G, C
{ ... };


[ Code équivalent à

A a;
C * pc(&a);
G * pg(dynamic_cast< G * >(pc));

]


Oui, en effet.

**** LE PROBLÈME ****

Le problème est que pc est non nul, mais que pg est nul.


G et C sont-ils polymorphes ?


Je dois le vérifier, mais j'espère que oui. Si ce n'est pas le cas pour
C cela peut s'arranger facilement. Malheureusement, si ce n'est pas le
cas pour les classes D, F, G je ne pourrai rien y faire : il s'agit de
classes fournies par les MFC de Microsoft, la dernière étant CWnd.

Je regarde ça demain, de retour sur mon lieu de travail.

Par exemple, est-ce que déclarer que C hérite aussi de G résoudrait le
problème ? Il faudrait bien sûr qu'il y ait un seul G au final dans A,
mais je ne sais pas comment déclarer cela.


L'héritage en diamant c'est :

class Base { public: virtual ~Base(); };

class Left: public virtual Base {};
class Right: public virtual Base {};

class Derived: public Left, public Right {};

Derived "a" une unique Base.


Et si à la place de Left j'ai une chaîne de classes Left1, Left2, Left3,
toutes doivent être polymorphes ? Est-ce que le « virtual » dans la
déclaration de l'héritage est obligatoire aussi ? Si oui, c'est perdu :
j'ai écrit les déclarations telles qu'elles sont, et je ne peux rien
faire pour changer les classes des MFC.

Cela dit, comment expliquer que le transtypage fonctionnait en VC++ 6.0
et ne fonctionne plus en Visual Studio 8.0 ?


Michel Decima
Le #306307

Cela dit, comment expliquer que le transtypage fonctionnait en VC++ 6.0
et ne fonctionne plus en Visual Studio 8.0 ?


J'ai essaye le code suivant, qui correspond a la version simplifiee avec
trois classes seulement, sur les compilateurs que j'ai a disposition ici
(xlC-6 et g++-4.1) :

#include <iostream>

class G {
public:
virtual ~G() {}
};

class C {
public:
virtual ~C() {}
};

class A : public G, C {
public:
void une_methode();
};

void une_fonction(C *pc)
{
G *pg = dynamic_cast<G *>(pc);
std::cout << pc << " " << pg << std::endl;
}

void A::une_methode() {
une_fonction( this );
}

int main() {
A a;
a.une_methode();
}

Ce programme me donne toujours une valeur nulle pour pg:

$ xlC transtypage.cpp && ./a.out
2ff21fb4 0

$ xlC -qlanglvl=strict98 transtypage.cpp && ./a.out
2ff21fb4 0

$ g++ transtypage.cpp && ./a.out
0x2ff21fa4 0

$ g++ -std=c++98 transtypage.cpp && ./a.out
0x2ff21fa4 0

Element interessant, xlC me signale que la classe A dérive de manière
privée de C, puisque c'est le comportement par défaut quand on ne
precise rien:

"transtypage.cpp", line 13.22: 1540-0198 (W) The omitted keyword
"private" is assumed for base class "C".

Juste pour faire taire le warning, je change la declaration de A qui va
maintenant deriver en public de C:

class A : public G, public C {
public:
void une_methode();
};

Et les tests donnent:

$ xlC transtypage.cpp && ./a.out
2ff21fa4 0

$ xlC -qlanglvl=strict98 transtypage.cpp && ./a.out
2ff21fa4 2ff21fa0

$ g++ transtypage.cpp && ./a.out
0x2ff21f94 0x2ff21f90

$ g++ -std=c++98 transtypage.cpp && ./a.out
0x2ff21f94 0x2ff21f90

Suite a la modification, xlC produit une valeur nulle en mode par
defaut, mais non nulle en mode standard. Pour g++, on a une valeur non
nulle dans les deux cas.

Bon, je n'explique pas la situation, mais il y aurait peut etre une
solution a ton probleme si tu peux passer la derivation de C en public
(dans le cas bien sur ou l'absence de mot-cle dans le code d'origine
n'est pas une coquille).

MD.

Olivier Miakinen
Le #306305

[...]

Juste pour faire taire le warning, je change la declaration de A qui va
maintenant deriver en public de C:

[...]

Suite a la modification, xlC produit une valeur nulle en mode par
defaut, mais non nulle en mode standard. Pour g++, on a une valeur non
nulle dans les deux cas.

Bon, je n'explique pas la situation, mais il y aurait peut etre une
solution a ton probleme si tu peux passer la derivation de C en public
(dans le cas bien sur ou l'absence de mot-cle dans le code d'origine
n'est pas une coquille).


L'absence du mot-clé « public » dans le code d'origine n'était pas une
coquille de ma part. Je viens de le rajouter, et en effet ça fonctionne
parfaitement maintenant !

Je suis toujours intéressé par une explication si quelqu'un en a une,
mais au moins je peux continuer à avancer sur le reste. Un grand merci
pour cela.

Publicité
Poster une réponse
Anonyme