Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Probleme de vitualisation

22 réponses
Avatar
Dominique Vaufreydaz
Bonjour,

j'ai ecrit une classe thread (suis pas le premier, hein ;-P) qui en fait contient une
fonction Run virtuelle pure qui doit etre redefini dans une classe dérivée. Rien
de vraiment difficile. Cette classe thread contient une fonction StartThread()

J'ai rajouté un booléen sur le constructeur du Thread. Si on lui passe true, il
demarre tout seul en appelant StartThread. Je me suis dit que de toute facon,
je ne peux pas instancier la classe Thread (car abstraite du fait du virtual ... = 0).

Le probleme qui se pose (sous g++ uniquement avec comme flags de compilation
-Werror -Wall -pedantic -std=c++98 -fPIC) c'est que je me retrouve avec
des erreurs (et pas a chacune des executions) du genre.
> pure virtual method called
> terminate called without an active exception
> Abort

A priori, pas de debordement ailleurs (je verifie encore) qui justifierait l'ecrasement
de la table des fonctions virtuelles. Des idées ? Notons que le meme mecanisme fonctionne
a merveille sous Visual Studio 2005. Peut-etre un flag a rajouter à g++ ?

Merci d'avance. Doms.

10 réponses

1 2 3
Avatar
Fabien LE LEZ
Même réponse que d'habitude : identifie et poste le code _minimal_ qui
reproduit le problème.
Avatar
Dominique Vaufreydaz
Bonjour,

Fabien LE LEZ wrote:
Même réponse que d'habitude : identifie et poste le code _minimal_ qui
reproduit le problème.


Toute l'attirail pour installer les choses se trouve ici :
http://gforge.inria.fr/frs/?group_id63

Il faut scons pour compiler et libxml2... Sinon, vous trouverez
la classe Thread ici :
http://www.cijoint.fr/cij10255612339302.zip

Concernant la code posant (parfois) probleme :
class Server : public Thread {
int id;
public:
Server(int id): Thread(true), id(id) {
}
virtual void Run()
{
// ici n'importe quoi, ca change rien !
for(;;;)
{
Sleep(10);
id++;
}
}
};

void maint()
{
new Server(i);
}

Voila. Doms.

Avatar
Michel Decima
Bonjour,

j'ai ecrit une classe thread (suis pas le premier, hein ;-P) qui en fait contient une
fonction Run virtuelle pure qui doit etre redefini dans une classe dérivée. Rien
de vraiment difficile. Cette classe thread contient une fonction StartThread()

J'ai rajouté un booléen sur le constructeur du Thread. Si on lui passe true, il
demarre tout seul en appelant StartThread. Je me suis dit que de toute facon,
je ne peux pas instancier la classe Thread (car abstraite du fait du virtual ... = 0).

Le probleme qui se pose (sous g++ uniquement avec comme flags de compilation
-Werror -Wall -pedantic -std=c++98 -fPIC) c'est que je me retrouve avec
des erreurs (et pas a chacune des executions) du genre.
pure virtual method called
terminate called without an active exception
Abort



Si tu appelle une fonction membre virtuelle dans un constructeur d'une
classe de base, c'est la version disponible dans la classe de base qui
est appelee, pas celle de la classe derivee. Si la fonction est
virtuelle pure, alors tu as un abort.

Ca peut paraitre tordu, mais c'est assez normal: le constructeur de la
classe de base va etre appelé avant celle de la classe derivee, et la
fonction virtuelle de la classe derivee utilise potentiellement des
membre de cette classe dérivee, qui ne seraient pas initialises lors
de l'appel par le constructeur de la base.

#include <iostream>

class Base {
public:
Base() {
func();
}

virtual void func() { std::cout << "Base" << std::endl; }
};

class Derived : public Base
{
public:
virtual void func() { std::cout << "Derived" << std::endl; }
};

int main() {
Derived d; // Affichera "Base".
}

Si je transforme Base::func() en fonction virtuelle pure, j'ai
le message suivant avec g++ (sans options particulieres):

pipo.C: In constructor 'Base::Base()':
pipo.C:6: warning: abstract virtual 'virtual void Base::func()' called
from constructor
ld: 0711-317 ERROR: Undefined symbol: .Base::func()

et xlC dit presque pareil:

"pipo.C", line 6.9: 1540-0285 (W) The expression calls the undefined
pure virtual function "Base::func()".
ld: 0711-317 ERROR: Undefined symbol: .Base::func()


A priori, pas de debordement ailleurs (je verifie encore) qui justifierait l'ecrasement
de la table des fonctions virtuelles. Des idées ? Notons que le meme mecanisme fonctionne
a merveille sous Visual Studio 2005. Peut-etre un flag a rajouter à g++ ?


Je dirais plutot qu'il faut trouver le flag necessaire a Visual Studio
pour qu'il cesse de faire n'importe quoi.


Avatar
Fabien LE LEZ
On Wed, 4 Apr 2007 08:53:17 +0200, "Dominique Vaufreydaz"
:

Il faut scons pour compiler et libxml2... Sinon, vous trouverez


Tu veux dire que l'erreur est cachée dans je ne sais combien de
milliers de lignes de code, et impossible à reproduire dans un code
plus simple ?

Dans ce cas, laisse tomber -- tu n'arriveras probablement jamais à
identifier le problème.

Avatar
Dominique Vaufreydaz
Bonjour,

Si tu appelle une fonction membre virtuelle dans un constructeur d'une
classe de base, c'est la version disponible dans la classe de base qui
est appelee, pas celle de la classe derivee. Si la fonction est
virtuelle pure, alors tu as un abort.
Ca peut paraitre tordu, mais c'est assez normal: le constructeur de la
classe de base va etre appelé avant celle de la classe derivee, et la
fonction virtuelle de la classe derivee utilise potentiellement des
membre de cette classe dérivee, qui ne seraient pas initialises lors
de l'appel par le constructeur de la base.


C'est bien ce que je pensais... Le truc qui me gonfle quand meme
c'est :
- que ca plante aps tout le temps
- que c'est pas dependant du code. Pour un meme exe, 2 executions
successivent donnes 2 comportement différents. Notons que
ca plante systematiquement si lancé via gdb ou valgrind...

Je dirais plutot qu'il faut trouver le flag necessaire a Visual Studio
pour qu'il cesse de faire n'importe quoi.


Mouai. Perso, ca perd de l'interet le virtuel dans ce cas la (meme
si c'est un peu particulier) et j'aurais aimé que l'initialisation des
fonctions virtuelles soitent faites avant l'appel des constructeurs des
classes meres...

Ca veut quand meme dire que le compilo devrait raler si on appelle
des fonctions virtuelles (pure) dans le constructeur d'une classe.
Ce que ne font pas ni cl (VS2005) ni g++...

Merci. Doms.

Avatar
Dominique Vaufreydaz
Salut,

Tu veux dire que l'erreur est cachée dans je ne sais combien de
milliers de lignes de code, et impossible à reproduire dans un code
plus simple ?


Non, que si tu veux tout, il te faut ca. Mais que ca merde qu'avec
le Thread (qui utilise Event soit).

Dans ce cas, laisse tomber -- tu n'arriveras probablement jamais à
identifier le problème.


Ben il faudrait bien car c'est moi qui ai fait ce code et que ca me
gonfle ! Surtout que le pure virtual method call j'ai un peu l'impression
que ca n'est pas vraiment de mon fait.

Reste que la reponse de Michel va dans le sens de ce que je pensais.
C'est un peu l'aspect random des choses qui m'embete.

Merci. Doms.

Avatar
James Kanze
On Apr 4, 7:46 am, "Dominique Vaufreydaz" wrote:

j'ai ecrit une classe thread (suis pas le premier, hein
;-P) qui en fait contient une fonction Run virtuelle pure
qui doit etre redefini dans une classe dérivée. Rien de
vraiment difficile. Cette classe thread contient une
fonction StartThread()


Ce n'est pas forcément une bonne idée. C'est le vieux argument
du modèle stratégie contre le modèle template : dans ce cas-ci,
je préfère nettement le modèle stratégie (qui permettrait aussi
par la suite de fournir des wrapper templatés pour des fonctions
ou des objets fonctionnels).

J'ai rajouté un booléen sur le constructeur du Thread. Si
on lui passe true, il demarre tout seul en appelant
StartThread.


Dans le constructeur de Thread ? Ça, c'est une très mauvaise
idée. Tu veux démarrer le thread avant que l'objet qui
l'implémente soit construit. (Curieusement, c'est une erreur
fréquente. La première version de SwingHelper, pour Java, la
faisait, par exemple.)

C'est une des raisons pourquoi je préfère de loin le modèle
stratégie ici.

Je me suis dit que de toute facon, je ne peux
pas instancier la classe Thread (car abstraite du fait du
virtual ... = 0).

Le probleme qui se pose (sous g++ uniquement avec comme flags de comp ilation
-Werror -Wall -pedantic -std=c++98 -fPIC) c'est que je me retrouve avec
des erreurs (et pas a chacune des executions) du genre.

pure virtual method called
terminate called without an active exception
Abort


A priori, pas de debordement ailleurs (je verifie encore)
qui justifierait l'ecrasement de la table des fonctions
virtuelles. Des idées ?


C'est un comportement indéfini. Lors de la construction de
Thread, le type dynamique de l'objet est Thread. Ce qui veut
dire que la résolution dynamique trouve la version de la
fonction dans Thread. Et la norme dit que si la résolution
dynamique trouve une fonction virtuelle pûre, c'est un
comportement indéfini (même si la fonction a une définition).

Notons que le meme mecanisme
fonctionne a merveille sous Visual Studio 2005.


C'est probablement le résultat d'une optimisation. Le
compilateur, sachant que la fonction est pûre virtuelle, et ne
peut pas être appelée par la résolution dynamique dans le
constructeur, se passe d'initialiser le vtable pour la classe de
base, mais l'initialise immédiatement pour la classe dérivée.

Comme j'ai dit, en revanche, c'est un comportement indéfini. Tu
ne peux pas y compter. Et dans ce cas-ci, tu ne veux pas t'y
compter non plus, parce que tu risques fort que la fonction
s'exécute sur l'objet avant même qu'on y est entré dans son
constructeur. (Ça dépend en fait du scheduleur ; je pourrais
bien imaginer un algorithme de scheduling où ça marcherait 99,9%
des fois, pour échouer 0,1%. C-à-d que ça marcherait dans tous
tes tests, pour échouer deux fois de suite lors de la démo
ultra-importante devant le client.)

Peut-etre
un flag a rajouter à g++ ?


Dans ce cas-ci, je crois que le comportement de g++ est le
mieux. Il ne te laisse pas croire que ça marche.

--
James Kanze (GABI Software) email:
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
Guillaume GOURDIN
Ce n'est pas forcément une bonne idée. C'est le vieux argument
du modèle stratégie contre le modèle template


Pourrais-je te demander ce que sont ces 2 modèles stp? Merci!

Avatar
Jean-Marc Bourguet
Guillaume GOURDIN writes:

Ce n'est pas forcément une bonne idée. C'est le vieux argument
du modèle stratégie contre le modèle template


Pourrais-je te demander ce que sont ces 2 modèles stp? Merci!


"Il n'y est pas de probleme qui ne puisse etre resolu par une indirection
de plus."

En gros, le modele template permet d'adapter le fonctionnement d'une classe
en substituant des membres virtuels. Le modele strategie permet d'adapter
le fonctionnement d'une classe en lui donnant en parametre une autre classe
contenant (uniquement?) les membres qui sont adaptables. Il est plus
complique, mais plus souple.

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
Laurent Deniau
Jean-Marc Bourguet wrote:
Guillaume GOURDIN writes:

Ce n'est pas forcément une bonne idée. C'est le vieux argument
du modèle stratégie contre le modèle template
Pourrais-je te demander ce que sont ces 2 modèles stp? Merci!



"Il n'y est pas de probleme qui ne puisse etre resolu par une indirection
de plus."

En gros, le modele template permet d'adapter le fonctionnement d'une classe
en substituant des membres virtuels. Le modele strategie permet d'adapter
le fonctionnement d'une classe en lui donnant en parametre une autre classe
contenant (uniquement?) les membres qui sont adaptables. Il est plus
complique, mais plus souple.


Sans compter que le second est configurable au runtime, ce qui peut
s'averer utile dans le cas de la gestion de threads.

a+, ld.



1 2 3