2009-08-11 4 views
5

Supposons que j'ai deux classes avec des membres identiques de deux bibliothèques différentes:Castings entre les classes congruentes sans rapport avec

namespace A { 
    struct Point3D { 
    float x,y,z; 
    }; 
} 

namespace B { 
    struct Point3D { 
    float x,y,z; 
    }; 
} 

Lorsque je tente cross-casting, il a travaillé:

A::Point3D pa = {3,4,5}; 
B::Point3D* pb = (B::Point3D*)&pa; 
cout << pb->x << " " << pb->y << " " << pb->z << endl; 

Dans quelles circonstances est-ce garanti pour fonctionner? Toujours? S'il vous plaît noter qu'il serait hautement indésirable de modifier une bibliothèque externe pour ajouter un pragma d'alignement ou quelque chose comme ça. J'utilise g ++ 4.3.2 sur Ubuntu 8.10.

+0

Si vous avez pa, pourquoi est-ce que vous avez besoin pb? Puisque n'importe quel endroit que vous utiliseriez pb pourrait simplement avoir & pa. Je ne suis pas sûr d'être clair sur le raisonnement ... – ezpz

Répondre

2

Si les structures que vous utilisez ne sont que des données et qu'aucun héritage n'est utilisé, je pense que cela devrait toujours fonctionner.

Tant qu'ils sont POD ça devrait aller. http://en.wikipedia.org/wiki/Plain_old_data_structures

Selon la norme (1.8.5)

« A moins qu'il est un champ BIT- (9,6), un objet plus dérivé doit avoir une taille non nulle et doit occuper un ou plusieurs octets de Les sous-objets de classe de base peuvent avoir une taille nulle.Un objet de type POD5) (3.9) doit occuper des octets contigus de stockage . "

Si elles occupent des octets contigus de stockage et ils sont les mêmes avec struct nom différent, un casting doit réussir

+0

En lisant cette citation de la norme, cela n'exclut pas le rembourrage, ni ne garantit que les deux structures utiliseront un rembourrage identique. Je suis d'accord, cela fonctionnera certainement dans la pratique. – jalf

+0

Si les deux structures sont définies de la même manière et que le compilateur est le même, le remplissage doit être identique. –

-2

Je sais exactement cela ne fonctionnerait pas:

  • deux struct a un alignement différen;
  • compilé avec différentes options RTTI

peut être une autre ...

+0

De http://en.wikipedia.org/wiki/RTTI: "RTTI est seulement disponible pour les classes qui sont polymorphes, ce qui signifie qu'ils ont au moins une méthode virtuelle." Votre inquiétude s'applique-t-elle toujours à POD? Dans quelles conditions auraient-ils un alignement différent? – Jann

+0

Ce n'est pas le cas. Il y a un mot-clé C++ typeid - qui devrait renvoyer un identifiant pour n'importe quel type (quel que soit l'usage), donc le compilateur doit incorporer un petit morceau à n'importe quelle structure. Encore plus, nous ne savons pas comment cette structure sera utilisée par le programmeur final. Donc, pour une seule instance, cette conversion sera correcte, mais pour array peut échouer. – Dewfy

+0

le compilateur n'intègre * rien * dans n'importe quelle structure. C'est facile à vérifier avec sizeof(). Vous pouvez facilement créer une structure de taille 1. Si vous créez une structure vide, elle peut même avoir la taille 0 si elle est utilisée comme classe de base. – jalf

0

Cette ligne devrait être:

B::Point3D* pb = (B::Point3D*)&pa; 

Notez le &. Je pense que ce que vous faites est reinterpret_cast entre deux pointeurs. En fait, vous pouvez réinterpréter_cast n'importe quel type de pointeur vers un autre, quel que soit le type des deux pointeurs. Mais c'est dangereux, et non portable.

Par exemple,

int x = 5; 
double* y = reinterpret_cast<double*>(&x); 

Vous allez juste avec le C-style, donc la deuxième ligne est en fait égal à:

double* z = (double*)&x; 

Je déteste le style C lors de la coulée en raison vous ne pouvez pas dire à la fin de la distribution d'un coup d'oeil :)


Dans quelles circonstances est-ce garanti pour fonctionner?

Ceci n'est pas réel coulée entre types.Par exemple,

int i = 5; 
float* f = reinterpret_cast<float*>(&i); 

maintenant f points au même endroit que i points. Donc, aucune conversion n'est faite. Lorsque vous déréférencer f, vous obtiendrez le flottant avec la même représentation binaire de l'entier i. C'est quatre octets sur ma machine.

+0

reinterpret_cast serait une mauvaise idée, car il n'est techniquement pas garanti d'aboutir à un pointeur vers la même adresse. static_cast (static_cast ()) garantit un pointeur vers la même adresse. – jalf

+0

Merci pour la correction. – Jann

2

Si deux structures POD commencent avec la même séquence de membres, la norme garantit que vous serez en mesure d'y accéder librement via une union. Vous pouvez stocker un A :: Point3D dans une union, puis lire depuis le membre B :: Point3D, tant que vous ne touchez que les membres qui font partie de la séquence commune initiale. (donc si une struct contenait int, int, int, float, et l'autre contenait int, int, int, int, vous seriez seulement autorisé à accéder aux trois premiers ints).

Cela semble donc une façon garantie de faire fonctionner votre code. Cela signifie également que la distribution devrait fonctionner, mais je ne suis pas sûr si cela est explicitement indiqué dans la norme.

Bien sûr, tout ceci suppose que les deux structures sont compilées avec le même compilateur pour garantir un ABI identique.

+0

+1: Excellent point. Ce serait un compilateur très étrange qui a décidé de traiter différemment POD-struct lorsqu'il est imbriqué dans une union ou non. –

0

Ce qui suit est assez sûr:

namespace A { 
    struct Point3D { 
    float x,y,z; 
    }; 
} 

namespace B { 
    typedef A::Point3D Point3D; 
} 

int main() { 
    A::Point3D a; 
    B::Point3D* b = &a; 

    return 0; 
} 
Questions connexes