[Kos-dev] Galere
d2
kos-dev@enix.org
17 Jan 2003 17:45:27 +0100
--=-=-=
Content-Type: text/plain; charset=iso-8859-15
Content-Transfer-Encoding: 8bit
Hello,
>>>>> "Thomas" == Thomas Petazzoni <thomas.petazzoni@enix.org> writes:
Thomas> En fait, j'ai regarde le code de libsupc++, et ca
Thomas> ressemble pas du tout a ce qu'on a fait jusqu'a maintenant
Thomas> au niveau de libcxxrt, donc je me posais des
Thomas> questions. Quelle est la difference entre les operator new
Thomas> (de libsupc++) et les __builtin_new que nous on definit ?
Ah ok, c'est ca votre "probleme" ! Ah ben alors tout va bien, car il
n'y en a plus, alors, de probleme ! Magique ? Non non...
Bon, alors reprenons. Avec gcc 2.x (en particulier 2.9x), le runtime
c++ est defini un peu partout et n'importe comment : les operators
new/delete/new[]/delete[] et tout le reste s'appellent
__builtin_new/... et donc ne correspondent pas aux noms "operator new"
d'un name mangling des familles. Bref, avec gcc 2.x, y'a pas de
runtime C++ bien propre bien isole, et les noms sont foireux. Ceci
dit, __builtin_new/... correspondent aux operateurs new/... (je pense
qu'il doit y avoir possibilite de definir des operator
new/delete/... ; mais dans ce cas la, je sais pas comment gcc reagit
vis a vis des __builtin_* ; mais honnetement nous on s'en fout un peu
de ca : __builtin_new/delete/... ca nous suffit point final).
Est arrive gcc 3.x. Avec lui, le runtime C++ est mieux isole, et se
trouve dans libsupc++ (sauf pour les parties liees aux exceptions, mais
on a -fno-exceptions). La, cette fois-ci, l'operator new s'appelle
vraiment (vrai mangling) "operator new". Meme chose pour delete et
cie.
Bref, pour gcc 2.9x, le runtime C++ utilise les fonctions
__builtin_*. Pour gcc 3.x, le runtime C++ utilise un equivalent de
libsupc++ ("operator new"...). Ce sont deux choses differentes, et on
utilise soit l'une, soit l'autre, suivant la version du compilo (mais
pas les 2 a la fois, ca sert a rien).
Thomas> Enfin si d'apres toi, les __builtin_new, __builtin_delete
Thomas> & co ca suffit, bin allons y ;-)
Pour gcc 2.x oui, pour gcc 3.x non. C'est pour ca que :
ls perso/devel/kos/kos/modules/libcxxrt 1217
CVS/ Makefile gcc-2.95.cc gcc-2.96.cc gcc-2.96.o gcc-3.cc libcxxrt.ro
Et a l'avenir je mettrai tout dans un seul .cc avec les
#if __GNUC__ == ...
qui vont bien.
Thomas> reinterpret_cast <std::size_t *> (base)[-1] =
THOMAS> tableau d'objets. Bon par contre le reinterpret_cast,
Thomas> j'avoue que j'ai un peu du mal ... Mais enfin bon.
Tu consideres que c'est un cast comme en C (ie, il s'agit de faire
comme ((size_t*)base)[-1] = ...). La difference entre static_cast et
reinterpret_cast est que static_cast sur un pointeur est un cast
controlé par le compilo qui tient compte des relations d'heritage,
avec recalcul d'adresse possible ; reinterprete_cast fait 1000000%
confiance au programmeur et n'effectue aucun recalcul d'adresse.
En gros, naturellement en C++ on peut faire du upcast sans precaution
(si class B : public class A, on peut passer un B *b a une fonction
qui prend en parametre A*). Mais pour faire du downcast (dire que
l'objet A* a qu'on a sous les yeux est en realite un objet de type B*)
on a le choix entre 2 mecanismes :
- dynamic_cast (RTTI necessaire) qui verifie la validite du
(down)cast a la compilation (si possible) ou en run-time (sinon),
- static_cast (pas besoin de RTTI) : qui ne verifie que la validite
semantique du cast (on verifie qu'on *sait* caster a en B* ; on ne
peut pas garantir que a sera forcement un objet de type B* valide)
Dans ces 2 cas, le static/dynamic_cast *peut* generer un objet qui a
une adresse differente (simple translation) de l'objet en parametre :
c'est le cas si le sous-objet de type A dans B est decale par rapport
a l'adresse de debut de B (inevitable dans le cas de l'heritage
multiple). Ces 2 mecanismes ne s'appliquent que la ou un cast
"implicite" est possible (relation d'heritage up/down, ou regles
internes au compilo [genre double/int...]). Bref, ce qu'il faut
retenir, c'est que dans les cas ou le compilo les accepte (ie cast
implicite possible), ces 2 mecanismes *peuvent* induire une
translation (au sens maths, ce n'est pas un anglicisme) d'adresse.
Or dans ton exemple il ne doit pas y avoir recalcul d'adresse (on veut
y aller bourrin a la C) => reinterpret_cast est la pour ca. Ca veut
dire : a l'adresse precise que j'indique, il y'a le debut d'un objet
de tel type, merci de ne pas refaire de calcul d'adresse avant de
pointer dessus.
Exemple :
--=-=-=
Content-Disposition: attachment; filename=a.cc
#include "stdio.h"
class A {
public:
char a[128];
};
class B {
public:
char b[256];
};
class C : public A, public B {
public:
char c[512];
};
int main()
{
B b, *pb;
C c, *pc;
printf("&c : %p\n", &c);
pb = dynamic_cast<B*>(&c);
printf("dyn<B>(&c) : %p\n", pb);
pb = static_cast<B*>(&c);
printf("static<B>(&c) : %p\n", pb);
pb = reinterpret_cast<B*>(&c);
printf("reint<B>(&c) : %p\n", pb);
return 0;
}
--=-=-=
La classe A est la juste pour decaler le debut du sous objet B dans C
(heritage multiple). On voit qu'effectivement, le debut de l'objet de
type B contenu dans c est decale de la taille de la classe A quand on
utilise les mecanismes propres de downcast :
&c : ffbee238
dyn<B>(&c) : ffbee2b8
static<B>(&c) : ffbee2b8
Par contre, pour le mecanisme de cast brutos (reinterpret_cast), on
force l'adresse de l'objet de type B considere a etre le debut de c
(ie on ecrase le sous-objet A de c) :
reint<B>(&c) : ffbee238
Pour finir, le compilo c++ interdira qu'on fasse un static_cast (et
meme un dynamic_cast quand il sait statiquement les types exacts de
tous les objets) d'un objet de type A vers un objet de type B (pas de
cast implicite puisqu'aucune relation d'heritage up/down). Il ne
ralera pas si on le force via reinterpret_cast.
Est-ce qu'on comprend qqch ?...
Bonne soiree,
--
d2
--=-=-=--