OVH Cloud OVH Cloud

RTTI

13 réponses
Avatar
Benoit Rousseau
Bonjour,
Quel est l'avantage de typeid( Object ) face à une fonction
Objet::type() retournant un entier par fixe pour chaque type d'Objet à
leur construction ?

Est ce que ca n'engendre pas trop de calculs ?
Est ce que les RTTI sont bien supportés par gcc 3.2.x ?

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

10 réponses

1 2
Avatar
Philippe Guglielmetti
l'avantage c'est que tu n'as pas besoin d'écrire une fonction par classe
(elle devrait être virtuelle pour fonctionner comme typeid) , puisque typeid
le fait exactement de cette manière.
Ca n'engendre pas plus de "calcul" qu'une méthode virtuelle normale et ça
ajoute donc grosso-modo un pointeur de plus à chaque objet.
Comme ça n'est pas très compliqué, tous les compilateurs le font facilement,
mais attention!
Le standard ne dit rien du type réel du retour! Chaque compilateur peut
implanter type_info comme il veut, certains ne promettent même pas de
renvoyer le même résultat après chaque compilation (ils risquent de
renuméroter les classes différemment chaque fois que tu en ajoute/enlève
une) donc le seul moyen d'utiliser RTTI de manière portable est
if (typeid(a)==typeid(b) || typeid(a)!=typeid(c))
Cela dit, à mon humble avis, un code C++ qui utilise le RTTI est un
programme mal foutu pour plusieurs raisons:
1) on peut toujours faire la même chose avec des méthodes virtuelles, qui ne
cassent pas le pipeline comme les tests
2) on augmente le risque d'erreur à l'exécution, surtout si en maintenant le
code on change la hiérarchie des classes : on est alors obligé de se
"souvenir" que les portions du code qui utilisent RTTI dépendent de cette
hiérarchie
--
Philippe Guglielmetti - www.dynabits.com
Avatar
Christophe Lephay
Philippe Guglielmetti wrote:
2) on augmente le risque d'erreur à l'exécution, surtout si en
maintenant le code on change la hiérarchie des classes : on est alors
obligé de se "souvenir" que les portions du code qui utilisent RTTI
dépendent de cette hiérarchie


Ceci dit, il vaut mieux des fois une erreur détectée à l'exécution qu'une
erreur pas détectée du tout...

Chris

Avatar
Benoit Dejean
Le Sat, 11 Oct 2003 14:06:36 +0200, Benoit Rousseau a écrit :

Bonjour,
Quel est l'avantage de typeid( Object ) face à une fonction
Objet::type() retournant un entier par fixe pour chaque type d'Objet à
leur construction ?

Est ce que ca n'engendre pas trop de calculs ?


si l'information est accessible au moment de la compilation, aucun. sinon
pas plus que ta méthode.

Est ce que les RTTI sont bien supportés par gcc 3.2.x ?


oui. typeid c'est bien, dynamic_cast aussi

Avatar
Arnaud Debaene
Philippe Guglielmetti wrote:

if (typeid(a)==typeid(b) || typeid(a)!=typeid(c))
Cela dit, à mon humble avis, un code C++ qui utilise le RTTI est un
programme mal foutu pour plusieurs raisons:
1) on peut toujours faire la même chose avec des méthodes virtuelles,
qui ne cassent pas le pipeline comme les tests


Est-ce que un test if casse d'avantatge le pipeline qu'un appel de foncion
virtuelle (donc via indirction de pointeur)? Ca ne me semble pas du tout
évident de faire des estimations là dessus surtout avec les processeurs
modernes....

Arnaud

Avatar
kanze
"Philippe Guglielmetti" wrote in message
news:<3f8819e7$0$3676$...

l'avantage c'est que tu n'as pas besoin d'écrire une fonction par
classe (elle devrait être virtuelle pour fonctionner comme typeid) ,
puisque typeid le fait exactement de cette manière.


L'avantage, surtout, c'est que dans une hièrarchie complexe, tu ne peux
pas oublié de fournir une fonction type pour une des classes dérivées.

Ca n'engendre pas plus de "calcul" qu'une méthode virtuelle normale et
ça ajoute donc grosso-modo un pointeur de plus à chaque objet.


Je n'ai jamais encore vue une implémentation où la présence de RTTI
avait la moindre incidence sur la taille d'un objet. Typiquement, il
ajoute un pointeur dans le vtbl (dont il n'y a qu'un par classe), c'est
tout.

Comme ça n'est pas très compliqué, tous les compilateurs le font
facilement, mais attention!

Le standard ne dit rien du type réel du retour!


Il dit que c'est un std::type_info. Classe qui est définie dans la
norme. L'implémentation a bien une liberté absolue en ce qui concerne
les extensions, mais sinon, elle n'a de la liberté qu'où la norme dit
que c'est défini par l'implémentation (notament, le contenu de la chaîne
renvoyée par name).

Chaque compilateur peut implanter type_info comme il veut, certains ne
promettent même pas de renvoyer le même résultat après chaque
compilation (ils risquent de renuméroter les classes différemment
chaque fois que tu en ajoute/enlève une)


Des explications ou des exemples, si te plaît. Il n'y a pas de
numérotation, à ce que je sache.

donc le seul moyen d'utiliser RTTI de manière portable est

if (typeid(a)==typeid(b) || typeid(a)!=typeid(c))


Ce qui est déjà assez utile dans certains cas.

Il y a aussi la fonction before, qu établit un ordre complet sur les
types, de façon à pouvoir utiliser des type_info comme clés dans un
std::set ou un std::map.

Sans parler, évidemment, des dynamic_cast, qui doivent représenter bien
plus de la moitié des utilisations de la RTTI.

Cela dit, à mon humble avis, un code C++ qui utilise le RTTI est un
programme mal foutu pour plusieurs raisons:

1) on peut toujours faire la même chose avec des méthodes virtuelles,


Mais il faut le faire soi-même -- avec les risques d'erreur que cela
implique.

qui ne cassent pas le pipeline comme les tests


Il n'y a rien qui casse le pipeline plus qu'un saut indirect -- c-à-d
comme un appel à une fonction virtuelle ou le retour d'une fonction. En
ce qui concerne les tests (if, etc.), la prédiction des branchements
fonctionne assez bien aujourd'hui.

2) on augmente le risque d'erreur à l'exécution, surtout si en
maintenant le code on change la hiérarchie des classes : on est alors
obligé de se "souvenir" que les portions du code qui utilisent RTTI
dépendent de cette hiérarchie


Ça dépend comment on l'utilise. C'est sûr que du code genre :

if ( typeid( *p ) == typeid( ClasseA ) ) {
} else if ( typeid( *p ) == typeid( ClasseB ) {
...

est à éviter.

En général, si on a besoin du type dérivé plus tard, le mieux, c'est de
ne pas le perdre, en gardant un pointeur ou une référence vers le type
dérivé. Mais ce n'est pas toujours pratique, et on peut être parfois
amener à poser la question, est-ce que l'objet implément telle ou telle
interface particulière, du genre :

Base* p ;
InterfaceA * pa = dynamic_cast< InterfaceA* >( p ) ;
if ( pa != NULL ) {
// Traitement particulier...
}

Ce qui ne nuit en rien à la maintenabilité de l'hièrarchie -- si on
ajoute par la suite des classes dérivées supplémentaires, ou bien elles
implémentent également l'InterfaceA, ou bien elles ne l'implémentent
pas.

L'autre utilisation type, c'est effectivement comme clé dans les
std::set ou des std::map, du genre :

struct TypeOrder
{
bool operator()( std::type_info const* lhs,
std::type_info const* rhs ) const
{
return lhs->before( *rhs ) ;
}
} ;

std::map< std::type_info const*, Whatever, TypeOrder >
umMap ;

Là, néanmoins, l'utilité est partiellement limitée par le fait que
l'ordre peut bien dépendre de l'édition de liens ou d'autre chose, et
qu'il n'est pas garanti d'être le même d'une compilation à l'autre.

Enfin, il y a la fonction name, dont le résultat est défini par
l'implémentation. Ce qui limite son utilité à des routines de trace et
d'autres sorties d'instrumentation, qui ne seront lues que par des êtres
humains. (À l'exception de g++, la plupart des compilateurs donne une
chaîne lisible).

--
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
Benoit Rousseau
wrote:
2) on augmente le risque d'erreur à l'exécution, surtout si en
maintenant le code on change la hiérarchie des classes : on est alors
obligé de se "souvenir" que les portions du code qui utilisent RTTI
dépendent de cette hiérarchie



Ça dépend comment on l'utilise. C'est sûr que du code genre :

if ( typeid( *p ) == typeid( ClasseA ) ) {
} else if ( typeid( *p ) == typeid( ClasseB ) {
...

est à éviter.


Pourquoi est ce à éviter ?
J'ai une classe Observer que l'on appelle par la fonction notify(Event*)
Il faut que je fasse ce genre de test pour déterminer ce que je vais
faire de mon Event*.
Sinon, il faudrait que je spécialise chacun des événements pour chacun
des Observers (une Factory pour chaque Observer ?) et ce serait beaucoup
de travail en plus...

Enfin, il y a la fonction name, dont le résultat est défini par
l'implémentation. Ce qui limite son utilité à des routines de trace et
d'autres sorties d'instrumentation, qui ne seront lues que par des êtres
humains. (À l'exception de g++, la plupart des compilateurs donne une
chaîne lisible).


Jusqu'à présent, il me retourne une chaine composé de la longueur du nom
de la classe suivit du nom de la classe : 5Event, 8Observer, ...


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


Avatar
Jean-Marc Bourguet
Benoit Rousseau writes:

wrote:
2) on augmente le risque d'erreur à l'exécution, surtout si en
maintenant le code on change la hiérarchie des classes : on est alors
obligé de se "souvenir" que les portions du code qui utilisent RTTI
dépendent de cette hiérarchie
Ça dépend comment on l'utilise. C'est sûr que du code genre :

if ( typeid( *p ) == typeid( ClasseA ) ) {
} else if ( typeid( *p ) == typeid( ClasseB ) {
...
est à éviter.


Pourquoi est ce à éviter ?
J'ai une classe Observer que l'on appelle par la fonction notify(Event*)
Il faut que je fasse ce genre de test pour déterminer ce que je vais faire
de mon Event*.
Sinon, il faudrait que je spécialise chacun des événements pour chacun des
Observers (une Factory pour chaque Observer ?) et ce serait beaucoup de
travail en plus...


La methode classique est celle du DP Visiteur, en attendant qu'on ait
du dispatch multiple. Il y a un certain nombre de variantes qui
supposent generalement qu'une des hierarchies est fermees (celles des
Event vraissemblablement dans ton cas).

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
Gabriel Dos Reis
writes:

| humains. (À l'exception de g++, la plupart des compilateurs donne une
| chaîne lisible).

g++ n'est pas la seule implémentation à implémenter l'ABI commune et
l'ABI commune requiert de retourner le nom manglé.

-- Gaby
Avatar
Gabriel Dos Reis
Benoit Rousseau writes:

| Jusqu'à présent, il me retourne une chaine composé de la longueur du nom
| de la classe suivit du nom de la classe : 5Event, 8Observer, ...

que tu peux démangler en utilisdant le démangleur qui va avec.

-- Gaby
Avatar
kanze
Benoit Rousseau wrote in message
news:<3f8a671b$0$15660$...
wrote:
2) on augmente le risque d'erreur à l'exécution, surtout si en
maintenant le code on change la hiérarchie des classes : on est
alors obligé de se "souvenir" que les portions du code qui utilisent
RTTI dépendent de cette hiérarchie


Ça dépend comment on l'utilise. C'est sûr que du code genre :

if ( typeid( *p ) == typeid( ClasseA ) ) {
} else if ( typeid( *p ) == typeid( ClasseB ) {
...

est à éviter.


Pourquoi est ce à éviter ?


Parce qu'il faut le modifier chaque fois que tu ajoutes une classe.

J'ai une classe Observer que l'on appelle par la fonction
notify(Event*) Il faut que je fasse ce genre de test pour déterminer
ce que je vais faire de mon Event*.


Il y a toujours le modèle de visiteur.

Alternativement : typiquement, chaque Observer n'est intéressé que dans
un nombre très limité d'Event. Nombre fermé, en plus -- le fait
d'ajouter un type d'évenemment ne veut pas dire que des classes
existantes ont à le traiter. Dans ce cas-là, tu n'as pas une chaîne avec
tous les possibilités -- tu n'en as qu'une ou deux. Et dans ce cas-là,
c'est acceptable. Bien que je le ferais plutôt avec des dynamic_cast.

Sinon, il faudrait que je spécialise chacun des événements pour chacun
des Observers (une Factory pour chaque Observer ?) et ce serait
beaucoup de travail en plus...


Avec le modèle visiteur, il suffit que les Observers aient une base
commune. Mais c'est vrai que tu es toujours amené à faire une liste des
types d'évenemment quelque part.

Enfin, il y a la fonction name, dont le résultat est défini par
l'implémentation. Ce qui limite son utilité à des routines de trace
et d'autres sorties d'instrumentation, qui ne seront lues que par
des êtres humains. (À l'exception de g++, la plupart des
compilateurs donne une chaîne lisible).


Jusqu'à présent, il me retourne une chaine composé de la longueur du
nom de la classe suivit du nom de la classe : 5Event, 8Observer, ...


Ce qui est prèsque lisible (mais moins bon que ce que font VC++ ou Sun
CC). En revanche, pour les types du genre int ou long...

La question, évidemment, c'est à quoi doit servir le nom. Dans la mésure
qu'il n'est pas normalisé, à peu près la seule chose auquel il peut
servir, c'est d'afficher. Donc, on privélège un format facilement
lisible par un homme.

--
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



1 2