Jean-Marc Bourguet writes:
| pour les 3 compilateurs. Je suis un peu confu de n'avoir pas essayé
| plus tot. Mais bon, j'ai (ré?)appris une particularité
| supplémentaire des virtuelles pures.
Oui, mais le fond demeure que tu as a un point : s'ils mettent une
adresse spéciale, ils pourraient en profiter pour mettre une adresse
utile, cela de la fonction virtuelle pure. Après tout, être une classe
abstraite veut juste dire qu'on ne peut pas créer un objet complet de
cette classe -- pas qu'il faille tout une armada spéciale.
Jean-Marc Bourguet <jm@bourguet.org> writes:
| pour les 3 compilateurs. Je suis un peu confu de n'avoir pas essayé
| plus tot. Mais bon, j'ai (ré?)appris une particularité
| supplémentaire des virtuelles pures.
Oui, mais le fond demeure que tu as a un point : s'ils mettent une
adresse spéciale, ils pourraient en profiter pour mettre une adresse
utile, cela de la fonction virtuelle pure. Après tout, être une classe
abstraite veut juste dire qu'on ne peut pas créer un objet complet de
cette classe -- pas qu'il faille tout une armada spéciale.
Jean-Marc Bourguet writes:
| pour les 3 compilateurs. Je suis un peu confu de n'avoir pas essayé
| plus tot. Mais bon, j'ai (ré?)appris une particularité
| supplémentaire des virtuelles pures.
Oui, mais le fond demeure que tu as a un point : s'ils mettent une
adresse spéciale, ils pourraient en profiter pour mettre une adresse
utile, cela de la fonction virtuelle pure. Après tout, être une classe
abstraite veut juste dire qu'on ne peut pas créer un objet complet de
cette classe -- pas qu'il faille tout une armada spéciale.
writes:Jean-Marc Bourguet wrote in message[les membres virtuels purs] sont des membres virtuels comme les
autres a deux differences pres:
- les descendants instanciables sont obliges de les supplanter
- on peut ne pas les definir (a l'exception du destructeur)
D'où est-ce que tu tiens ça ?
J'ai l'impression que tu confonds "on peut ne pas" avec "on ne peut
pas".
En fait, à peu près la seule véritable différence entre une fonction
virtuelle pure et une virtuelle tout courte, c'est que la résolution
*dynamique* ne doit jamais rétrouver une fonction virtuelle pure ;
class A {
~A() { g(); }
virtual void f() = 0;
void g();
};
void A::g() {
f();
}
void A::f() {
std::cout << "In A::f(), a pure virtual functionn";
}
Ou bien tu ne consideres pas l'appel de f dans g comme etant une
resolution dynamique?
kanze@gabi-soft.fr writes:
Jean-Marc Bourguet <jm@bourguet.org> wrote in message
[les membres virtuels purs] sont des membres virtuels comme les
autres a deux differences pres:
- les descendants instanciables sont obliges de les supplanter
- on peut ne pas les definir (a l'exception du destructeur)
D'où est-ce que tu tiens ça ?
J'ai l'impression que tu confonds "on peut ne pas" avec "on ne peut
pas".
En fait, à peu près la seule véritable différence entre une fonction
virtuelle pure et une virtuelle tout courte, c'est que la résolution
*dynamique* ne doit jamais rétrouver une fonction virtuelle pure ;
class A {
~A() { g(); }
virtual void f() = 0;
void g();
};
void A::g() {
f();
}
void A::f() {
std::cout << "In A::f(), a pure virtual functionn";
}
Ou bien tu ne consideres pas l'appel de f dans g comme etant une
resolution dynamique?
writes:Jean-Marc Bourguet wrote in message[les membres virtuels purs] sont des membres virtuels comme les
autres a deux differences pres:
- les descendants instanciables sont obliges de les supplanter
- on peut ne pas les definir (a l'exception du destructeur)
D'où est-ce que tu tiens ça ?
J'ai l'impression que tu confonds "on peut ne pas" avec "on ne peut
pas".
En fait, à peu près la seule véritable différence entre une fonction
virtuelle pure et une virtuelle tout courte, c'est que la résolution
*dynamique* ne doit jamais rétrouver une fonction virtuelle pure ;
class A {
~A() { g(); }
virtual void f() = 0;
void g();
};
void A::g() {
f();
}
void A::f() {
std::cout << "In A::f(), a pure virtual functionn";
}
Ou bien tu ne consideres pas l'appel de f dans g comme etant une
resolution dynamique?
writes:
| Gabriel Dos Reis wrote in message
| news:...
| > Jean-Marc Bourguet writes:
| [...]
| > | pour les 3 compilateurs. Je suis un peu confu de n'avoir pas
| > | essayé plus tot. Mais bon, j'ai (ré?)appris une particularité
| > | supplémentaire des virtuelles pures.
| > Oui, mais le fond demeure que tu as a un point : s'ils mettent une
| > adresse spéciale, ils pourraient en profiter pour mettre une
| > adresse utile, cela de la fonction virtuelle pure. Après tout,
| > être une classe abstraite veut juste dire qu'on ne peut pas créer
| > un objet complet de cette classe -- pas qu'il faille tout une
| > armada spéciale.
| Le problème, c'est qu'on travaille toujours avec une idée précise de
| ce que c'est la compilation séparée (sauf peut-être pour certains
| aspects des templates). Donc, lors que le compilateur génère le
| vtbl, il ne sait
La compilation séparée a peu de chose à avoir dans cette histoire --
le fond du problème lorsque tu l'analyses bien est que le même que
pour « ::operator new ».
| pas si la fonction pûre est définie ou non. (Pour les virtuelles
| non-pûre, c'est un comportement indéfini si elle n'est pas définie.)
| S'il met l'adresse de la fonction, et qu'elle n'est pas définie, il
| y aurait une erreur lors de l'édition de liens.
Cela n'a aucune expèce d'importance.
La technologie nécessaire pour faire marcher ce dont Jean-Marc et moi
proposons/discutons est déjà requise implémenter le remplacement de
« operator new » et consort : si l'utilisateur fournit une définition
quelque part dans le programme alors cette définition prend précédence
sur celle de l'implémentation. C'est exactement la même chose ici : si
la fonction virtuelle pure est définie quelque part alors cette
définition prend précédence sur celle donnée par l'implémentation (qui
usuellement est avoter le programme avec un message d'erreur disant
qu'une fonction virtuelle pure a été appelée).
| Ceci dit, ça ne justifie pas un comportement indéfini. Mais le
| comportement défini doit être quelque chose du genre : appelle
| terminate(), ou « outputs an implementation defined message to cout,
| then calls abort() ».
Si on doit faire ça, autant faire quelque chose d'utile -- parce que
l'effort est le même.
kanze@gabi-soft.fr writes:
| Gabriel Dos Reis <gdr@integrable-solutions.net> wrote in message
| news:<m3d5y4w9qx.fsf@uniton.integrable-solutions.net>...
| > Jean-Marc Bourguet <jm@bourguet.org> writes:
| [...]
| > | pour les 3 compilateurs. Je suis un peu confu de n'avoir pas
| > | essayé plus tot. Mais bon, j'ai (ré?)appris une particularité
| > | supplémentaire des virtuelles pures.
| > Oui, mais le fond demeure que tu as a un point : s'ils mettent une
| > adresse spéciale, ils pourraient en profiter pour mettre une
| > adresse utile, cela de la fonction virtuelle pure. Après tout,
| > être une classe abstraite veut juste dire qu'on ne peut pas créer
| > un objet complet de cette classe -- pas qu'il faille tout une
| > armada spéciale.
| Le problème, c'est qu'on travaille toujours avec une idée précise de
| ce que c'est la compilation séparée (sauf peut-être pour certains
| aspects des templates). Donc, lors que le compilateur génère le
| vtbl, il ne sait
La compilation séparée a peu de chose à avoir dans cette histoire --
le fond du problème lorsque tu l'analyses bien est que le même que
pour « ::operator new ».
| pas si la fonction pûre est définie ou non. (Pour les virtuelles
| non-pûre, c'est un comportement indéfini si elle n'est pas définie.)
| S'il met l'adresse de la fonction, et qu'elle n'est pas définie, il
| y aurait une erreur lors de l'édition de liens.
Cela n'a aucune expèce d'importance.
La technologie nécessaire pour faire marcher ce dont Jean-Marc et moi
proposons/discutons est déjà requise implémenter le remplacement de
« operator new » et consort : si l'utilisateur fournit une définition
quelque part dans le programme alors cette définition prend précédence
sur celle de l'implémentation. C'est exactement la même chose ici : si
la fonction virtuelle pure est définie quelque part alors cette
définition prend précédence sur celle donnée par l'implémentation (qui
usuellement est avoter le programme avec un message d'erreur disant
qu'une fonction virtuelle pure a été appelée).
| Ceci dit, ça ne justifie pas un comportement indéfini. Mais le
| comportement défini doit être quelque chose du genre : appelle
| terminate(), ou « outputs an implementation defined message to cout,
| then calls abort() ».
Si on doit faire ça, autant faire quelque chose d'utile -- parce que
l'effort est le même.
writes:
| Gabriel Dos Reis wrote in message
| news:...
| > Jean-Marc Bourguet writes:
| [...]
| > | pour les 3 compilateurs. Je suis un peu confu de n'avoir pas
| > | essayé plus tot. Mais bon, j'ai (ré?)appris une particularité
| > | supplémentaire des virtuelles pures.
| > Oui, mais le fond demeure que tu as a un point : s'ils mettent une
| > adresse spéciale, ils pourraient en profiter pour mettre une
| > adresse utile, cela de la fonction virtuelle pure. Après tout,
| > être une classe abstraite veut juste dire qu'on ne peut pas créer
| > un objet complet de cette classe -- pas qu'il faille tout une
| > armada spéciale.
| Le problème, c'est qu'on travaille toujours avec une idée précise de
| ce que c'est la compilation séparée (sauf peut-être pour certains
| aspects des templates). Donc, lors que le compilateur génère le
| vtbl, il ne sait
La compilation séparée a peu de chose à avoir dans cette histoire --
le fond du problème lorsque tu l'analyses bien est que le même que
pour « ::operator new ».
| pas si la fonction pûre est définie ou non. (Pour les virtuelles
| non-pûre, c'est un comportement indéfini si elle n'est pas définie.)
| S'il met l'adresse de la fonction, et qu'elle n'est pas définie, il
| y aurait une erreur lors de l'édition de liens.
Cela n'a aucune expèce d'importance.
La technologie nécessaire pour faire marcher ce dont Jean-Marc et moi
proposons/discutons est déjà requise implémenter le remplacement de
« operator new » et consort : si l'utilisateur fournit une définition
quelque part dans le programme alors cette définition prend précédence
sur celle de l'implémentation. C'est exactement la même chose ici : si
la fonction virtuelle pure est définie quelque part alors cette
définition prend précédence sur celle donnée par l'implémentation (qui
usuellement est avoter le programme avec un message d'erreur disant
qu'une fonction virtuelle pure a été appelée).
| Ceci dit, ça ne justifie pas un comportement indéfini. Mais le
| comportement défini doit être quelque chose du genre : appelle
| terminate(), ou « outputs an implementation defined message to cout,
| then calls abort() ».
Si on doit faire ça, autant faire quelque chose d'utile -- parce que
l'effort est le même.
writes:
| Jean-Marc Bourguet wrote in message
| news:...
| > Gabriel Dos Reis writes:
| > > | > #include <iostream>
| > > | > struct A {
| > > | > virtual void f() = 0;
| > > | > virtual ~A() { f(); }
| > > | > };
| > > | > void A::f() { std::cout << "coucou !n"; }
| > > | Oui, je vois, tu as raison aussi :-)
| > > Non. Je n'ai pas raison : la fonction est *définie*, mais le
| > > code invoque un fonctionnement indéfini (la plupart des
| > > compilateurs l'accepteront sur la base du méchanisme d'appel).
| > Evidemment ils ont ete mettre en 10.4/3 et une clause qui
| > contredit ce qu'on peut penser en ne regardant que 12.7 (et en
| > particulier 12.7/3).
| > Je comprends que l'effet soint indefini quand la fonction n'est
| > pas definie, mais si elle l'est ca me choque un peu.
| Je ne sais pas. C'est le résultat d'un ensemble de règles, chacune
| simple et logique en soi :
| - la résolution dynamique ne trouve jamais une fonction virtuelle
| pûre,
Cette règle ne me semble ni simple ni logique en soi.
kanze@gabi-soft.fr writes:
| Jean-Marc Bourguet <jm@bourguet.org> wrote in message
| news:<pxbekik4hvk.fsf@news.bourguet.org>...
| > Gabriel Dos Reis <gdr@integrable-solutions.net> writes:
| > > | > #include <iostream>
| > > | > struct A {
| > > | > virtual void f() = 0;
| > > | > virtual ~A() { f(); }
| > > | > };
| > > | > void A::f() { std::cout << "coucou !n"; }
| > > | Oui, je vois, tu as raison aussi :-)
| > > Non. Je n'ai pas raison : la fonction est *définie*, mais le
| > > code invoque un fonctionnement indéfini (la plupart des
| > > compilateurs l'accepteront sur la base du méchanisme d'appel).
| > Evidemment ils ont ete mettre en 10.4/3 et une clause qui
| > contredit ce qu'on peut penser en ne regardant que 12.7 (et en
| > particulier 12.7/3).
| > Je comprends que l'effet soint indefini quand la fonction n'est
| > pas definie, mais si elle l'est ca me choque un peu.
| Je ne sais pas. C'est le résultat d'un ensemble de règles, chacune
| simple et logique en soi :
| - la résolution dynamique ne trouve jamais une fonction virtuelle
| pûre,
Cette règle ne me semble ni simple ni logique en soi.
writes:
| Jean-Marc Bourguet wrote in message
| news:...
| > Gabriel Dos Reis writes:
| > > | > #include <iostream>
| > > | > struct A {
| > > | > virtual void f() = 0;
| > > | > virtual ~A() { f(); }
| > > | > };
| > > | > void A::f() { std::cout << "coucou !n"; }
| > > | Oui, je vois, tu as raison aussi :-)
| > > Non. Je n'ai pas raison : la fonction est *définie*, mais le
| > > code invoque un fonctionnement indéfini (la plupart des
| > > compilateurs l'accepteront sur la base du méchanisme d'appel).
| > Evidemment ils ont ete mettre en 10.4/3 et une clause qui
| > contredit ce qu'on peut penser en ne regardant que 12.7 (et en
| > particulier 12.7/3).
| > Je comprends que l'effet soint indefini quand la fonction n'est
| > pas definie, mais si elle l'est ca me choque un peu.
| Je ne sais pas. C'est le résultat d'un ensemble de règles, chacune
| simple et logique en soi :
| - la résolution dynamique ne trouve jamais une fonction virtuelle
| pûre,
Cette règle ne me semble ni simple ni logique en soi.
writes:
| Que veux-tu d'autre ?
Quelque chose d'utile.
| Dans la pratique, quand le compilateur génère le
| vtbl, il ne peut pas savoir si A::f() est définie ou non.
Il se débrouille bien avec « operator new », il peut aussi se
débrouiller pour faire quelque d'utile, qui nécessite juste le mÊme
effort.
kanze@gabi-soft.fr writes:
| Que veux-tu d'autre ?
Quelque chose d'utile.
| Dans la pratique, quand le compilateur génère le
| vtbl, il ne peut pas savoir si A::f() est définie ou non.
Il se débrouille bien avec « operator new », il peut aussi se
débrouiller pour faire quelque d'utile, qui nécessite juste le mÊme
effort.
writes:
| Que veux-tu d'autre ?
Quelque chose d'utile.
| Dans la pratique, quand le compilateur génère le
| vtbl, il ne peut pas savoir si A::f() est définie ou non.
Il se débrouille bien avec « operator new », il peut aussi se
débrouiller pour faire quelque d'utile, qui nécessite juste le mÊme
effort.
Gabriel Dos Reis wrote in message
news:...writes:| Jean-Marc Bourguet wrote in message
| news:...
| > Gabriel Dos Reis writes:| > > | > #include <iostream>| > > | > struct A {
| > > | > virtual void f() = 0;
| > > | > virtual ~A() { f(); }
| > > | > };| > > | > void A::f() { std::cout << "coucou !n"; }| > > | Oui, je vois, tu as raison aussi :-)| > > Non. Je n'ai pas raison : la fonction est *définie*, mais le
| > > code invoque un fonctionnement indéfini (la plupart des
| > > compilateurs l'accepteront sur la base du méchanisme d'appel).| > Evidemment ils ont ete mettre en 10.4/3 et une clause qui
| > contredit ce qu'on peut penser en ne regardant que 12.7 (et en
| > particulier 12.7/3).| > Je comprends que l'effet soint indefini quand la fonction n'est
| > pas definie, mais si elle l'est ca me choque un peu.| Je ne sais pas. C'est le résultat d'un ensemble de règles, chacune
| simple et logique en soi :
| - la résolution dynamique ne trouve jamais une fonction virtuelle
| pûre,Cette règle ne me semble ni simple ni logique en soi.
Alors, avec quoi est-ce que tu proposes de la remplacer, étant donné
qu'on n'est pas obligé à fournir une définition pour une fonction
virtuelle pure. Qu'on soit obligé à fournir une définition ? Ou que la
résolution dynamique trouve peut-être une fonction virtuelle pûre, et
peut-être pas, selon ce qui se trouve dans d'autres unités de
compilation, que tu ne connais pas ?
Gabriel Dos Reis <gdr@integrable-solutions.net> wrote in message
news:<m3y8grqtap.fsf@uniton.integrable-solutions.net>...
kanze@gabi-soft.fr writes:
| Jean-Marc Bourguet <jm@bourguet.org> wrote in message
| news:<pxbekik4hvk.fsf@news.bourguet.org>...
| > Gabriel Dos Reis <gdr@integrable-solutions.net> writes:
| > > | > #include <iostream>
| > > | > struct A {
| > > | > virtual void f() = 0;
| > > | > virtual ~A() { f(); }
| > > | > };
| > > | > void A::f() { std::cout << "coucou !n"; }
| > > | Oui, je vois, tu as raison aussi :-)
| > > Non. Je n'ai pas raison : la fonction est *définie*, mais le
| > > code invoque un fonctionnement indéfini (la plupart des
| > > compilateurs l'accepteront sur la base du méchanisme d'appel).
| > Evidemment ils ont ete mettre en 10.4/3 et une clause qui
| > contredit ce qu'on peut penser en ne regardant que 12.7 (et en
| > particulier 12.7/3).
| > Je comprends que l'effet soint indefini quand la fonction n'est
| > pas definie, mais si elle l'est ca me choque un peu.
| Je ne sais pas. C'est le résultat d'un ensemble de règles, chacune
| simple et logique en soi :
| - la résolution dynamique ne trouve jamais une fonction virtuelle
| pûre,
Cette règle ne me semble ni simple ni logique en soi.
Alors, avec quoi est-ce que tu proposes de la remplacer, étant donné
qu'on n'est pas obligé à fournir une définition pour une fonction
virtuelle pure. Qu'on soit obligé à fournir une définition ? Ou que la
résolution dynamique trouve peut-être une fonction virtuelle pûre, et
peut-être pas, selon ce qui se trouve dans d'autres unités de
compilation, que tu ne connais pas ?
Gabriel Dos Reis wrote in message
news:...writes:| Jean-Marc Bourguet wrote in message
| news:...
| > Gabriel Dos Reis writes:| > > | > #include <iostream>| > > | > struct A {
| > > | > virtual void f() = 0;
| > > | > virtual ~A() { f(); }
| > > | > };| > > | > void A::f() { std::cout << "coucou !n"; }| > > | Oui, je vois, tu as raison aussi :-)| > > Non. Je n'ai pas raison : la fonction est *définie*, mais le
| > > code invoque un fonctionnement indéfini (la plupart des
| > > compilateurs l'accepteront sur la base du méchanisme d'appel).| > Evidemment ils ont ete mettre en 10.4/3 et une clause qui
| > contredit ce qu'on peut penser en ne regardant que 12.7 (et en
| > particulier 12.7/3).| > Je comprends que l'effet soint indefini quand la fonction n'est
| > pas definie, mais si elle l'est ca me choque un peu.| Je ne sais pas. C'est le résultat d'un ensemble de règles, chacune
| simple et logique en soi :
| - la résolution dynamique ne trouve jamais une fonction virtuelle
| pûre,Cette règle ne me semble ni simple ni logique en soi.
Alors, avec quoi est-ce que tu proposes de la remplacer, étant donné
qu'on n'est pas obligé à fournir une définition pour une fonction
virtuelle pure. Qu'on soit obligé à fournir une définition ? Ou que la
résolution dynamique trouve peut-être une fonction virtuelle pûre, et
peut-être pas, selon ce qui se trouve dans d'autres unités de
compilation, que tu ne connais pas ?
Jean-Marc Bourguet wrote:Laurent Deniau writes:Donc B* devient un A* et reste un A*.
[...]
delete a;
peut donc être compilé en qqch du genre (j'ai jamais
travaillé sur un compilateur C++, il y a peut-être des
choses que j'oublie):
__base = a->__get_most_derived_object();
__sz = a->__get_size_of_most_derived_object();
__delete = a->__get_delete_operator();
a->__destructor_in_charge_of_virtual_members(__base);
(*__delete)(__base, __sz);
Je pense que tu as raison. Je viens de faire un test (code a la fin) qui
montre que les deux sont compatibles: transition du type dans les
ctor/dtor + delete/RTTI virtuels (j'aurais du commencer par la). Par
contre aux vues de ce que Gaby a explique, je ne vois pas comment le
compilo peut s'en sortir sans reaffecter le __vtbl de l'objet lorsqu'il
mute l'objet entre deux dtors. Si en entrant dans A::~A(), this qui
etait un B* devient vraiment un A*, __vtbl doit necessairement changer.
Si ce n'est pas ce qu'il fait, j'aimerais bien que qqun m'explique
comment le compilo s'en sort avec l'appel de f2() dans A::f1()?
Jean-Marc Bourguet wrote:
Laurent Deniau <Laurent.Deniau@cern.ch> writes:
Donc B* devient un A* et reste un A*.
[...]
delete a;
peut donc être compilé en qqch du genre (j'ai jamais
travaillé sur un compilateur C++, il y a peut-être des
choses que j'oublie):
__base = a->__get_most_derived_object();
__sz = a->__get_size_of_most_derived_object();
__delete = a->__get_delete_operator();
a->__destructor_in_charge_of_virtual_members(__base);
(*__delete)(__base, __sz);
Je pense que tu as raison. Je viens de faire un test (code a la fin) qui
montre que les deux sont compatibles: transition du type dans les
ctor/dtor + delete/RTTI virtuels (j'aurais du commencer par la). Par
contre aux vues de ce que Gaby a explique, je ne vois pas comment le
compilo peut s'en sortir sans reaffecter le __vtbl de l'objet lorsqu'il
mute l'objet entre deux dtors. Si en entrant dans A::~A(), this qui
etait un B* devient vraiment un A*, __vtbl doit necessairement changer.
Si ce n'est pas ce qu'il fait, j'aimerais bien que qqun m'explique
comment le compilo s'en sort avec l'appel de f2() dans A::f1()?
Jean-Marc Bourguet wrote:Laurent Deniau writes:Donc B* devient un A* et reste un A*.
[...]
delete a;
peut donc être compilé en qqch du genre (j'ai jamais
travaillé sur un compilateur C++, il y a peut-être des
choses que j'oublie):
__base = a->__get_most_derived_object();
__sz = a->__get_size_of_most_derived_object();
__delete = a->__get_delete_operator();
a->__destructor_in_charge_of_virtual_members(__base);
(*__delete)(__base, __sz);
Je pense que tu as raison. Je viens de faire un test (code a la fin) qui
montre que les deux sont compatibles: transition du type dans les
ctor/dtor + delete/RTTI virtuels (j'aurais du commencer par la). Par
contre aux vues de ce que Gaby a explique, je ne vois pas comment le
compilo peut s'en sortir sans reaffecter le __vtbl de l'objet lorsqu'il
mute l'objet entre deux dtors. Si en entrant dans A::~A(), this qui
etait un B* devient vraiment un A*, __vtbl doit necessairement changer.
Si ce n'est pas ce qu'il fait, j'aimerais bien que qqun m'explique
comment le compilo s'en sort avec l'appel de f2() dans A::f1()?