La réflexion de Pollux disant que les adresses étaient forcément connues par le compilateur à compile-time me titillait sous la douche ce matin, et je suis arrivé à ça :
---------------8>-------------------------- template <class E> struct Any { static const E obj; static const Any<E>& obj_ref; static const int offset;
E& exact() { return *(E*)((char*)(this) - offset); }
// For testing purposes, makes the address of Any<E> different than // E's address. int i; };
// Nécessite un constructeur par défaut et stocke un objet par classe // (pas par instance). Y'a-t-il moyen de contourner ça ? template <class E> const Any<E> obj = E();
template <class E> const Any<E>& Any<E>::obj_ref = Any<E>::obj;
// Pourrait etre calculé directement dans exact(). template <class E> const int Any<E>::offset = (char*)(void*)(&Any<E>::obj_ref) - (char*)(void*)(&Any<E>::obj); ---------------8>--------------------------
L'idée est de prendre la différence d'adresse entre un objet de type Exact et une référence dessus upcastée en Any<Exact>. Vu que ces membres sont statiques et constants, le compilateur connaît leurs adresses à la compilation et peut calculer l'offset à la compilation également, et donc l'adresse du type exact.
Autre avantage, plus besoin de magouiller dans les constructeurs des classes filles, la classe de base Any se débrouille toute seule !
Problème majeur pour l'instant, ça nécessite un moyen d'instancier un objet exact dans Any, et ça stocke une instance de classe par classe. Mais si on a des types qui peuvent être vides (genre les images), ca reste cool.
J'en ai profité pour faire qq benchs sur un truc proche de ce qu'on a dans olena (affectation de tous les points d'une image 10000x10000 en passant par l'operateur [] de la classe de base Image), ca fait assez peur avec 3.3 pour les hiérarchies multiples, mais 3.4 sera notre ami :
/*--------. | g++-3.3 | `--------*/
Avec héritage multiple (emulé pour le code en static_cast): ======================
Static_cast 1.42s Pointeur sur exact 2.35s Offset dynamique 2.55s Offset statique 2.35s
Sans héritage multiple ======================
Static_cast 1.42s Pointeur sur exact 1.45s Offset dynamique 2.55s Offset statique 1.42s
/*--------. | g++-3.4 | `--------*/
Avec héritage multiple (emulé pour le code en static_cast): ======================
Static_cast 1.40s Pointeur sur exact 2.36s Offset dynamique 2.57s Offset statique 1.38s
Sans héritage multiple ======================
Static_cast 1.40s Pointeur sur exact 1.42s Offset dynamique 2.56s Offset statique 1.41s
Bref, dans tous les cas la version offset dynamique est à la rue, et le pointeur sur this est toujours à la rue aussi avec de l'héritage multiple. Avec 3.4, l'offset statique s'en sort bien meme en héritage multiple, alors qu'il se vautre comme les autres avec 3.3.
Enfin bon, tout ça est a prendre avec des pincettes, ca varie beaucoup selon les compilos.
PS: ca ne marche pas avec 2.95, l'offset vaut toujours 0 !