2009-08-11 13 views
5

Supponiamo di avere due classi con membri identici da due librerie differenti:Casting tra classi congruenti indipendenti

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

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

Quando provo cross-casting, ha funzionato:

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

In quali circostanze è questo garantito a lavorare? Sempre? Si noti che sarebbe altamente indesiderabile modificare una libreria esterna per aggiungere un pragma di allineamento o qualcosa del genere. Sto usando g ++ 4.3.2 su Ubuntu 8.10.

+0

Se avete pa, perché è che avete bisogno di pb? Dal momento che qualsiasi posto che utilizzeresti pb potrebbe semplicemente avere & pa. Non sono sicuro di essere chiaro sul ragionamento ... – ezpz

risposta

2

Se le strutture che si utilizzano sono solo dati e non viene utilizzata alcuna ereditarietà, penso che dovrebbe sempre funzionare.

Fintanto che sono POD, dovrebbe essere ok. http://en.wikipedia.org/wiki/Plain_old_data_structures

Secondo lo standard (1.8.5)

"meno che non sia un campo bit-fi (9.6), un oggetto più derivato deve avere una dimensione diversa da zero e deve occupare uno o più byte di Memoria . Gli oggetti secondari di classe base possono avere dimensione zero. Un oggetto di POD5) Il tipo (3.9) occupa byte contigui di memoria ".

se occupano byte contigui di stoccaggio e sono la stessa struct con nome diverso, un cast dovrebbe riuscire

+0

Mentre leggo quella citazione dallo standard, non esclude il riempimento, né garantisco che le due strutture utilizzino il riempimento identico. Sono d'accordo, funzionerà quasi sicuramente nella pratica. – jalf

+0

Se entrambe le strutture sono definite uguali e il compilatore è uguale, il riempimento deve essere identico. –

-2

so esattamente che non avrebbe funzionato:

  • sia struct ha allineamento differen;
  • compilato con diverse opzioni RTTI

potrebbe essere qualche altra cosa ...

+0

Da http://en.wikipedia.org/wiki/RTTI: "RTTI è disponibile solo per le classi che sono polimorfiche, il che significa che hanno almeno un metodo virtuale." La tua preoccupazione si applica ancora al POD? In quali condizioni avrebbero un allineamento diverso? – Jann

+0

Non è così. C'è una parola chiave di tipo C++ che dovrebbe restituire l'identificatore per qualsiasi tipo (indipendentemente dall'utilizzo), quindi il compilatore deve incorporare piccoli blocchi in qualsiasi struttura. Ancor di più, non sappiamo come verrà utilizzata questa struttura dal programmatore finale. Quindi per la singola istanza questa conversione sarà corretta, ma per la matrice potrebbe fallire. – Dewfy

+0

il compilatore * non * incorpora qualcosa in qualsiasi struttura. È facile verificarlo con sizeof(). È possibile creare facilmente una struttura di dimensione 1. Se si crea una struttura vuota, può anche avere la dimensione 0 se è usata come classe base. – jalf

0

Questa linea dovrebbe essere:

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

Annotare la &. Penso che quello che stai facendo è un reinterpret_cast tra due puntatori. In effetti puoi reinterpretare_cast qualsiasi tipo di puntatore a un altro, indipendentemente dal tipo dei due puntatori. Ma questo pericoloso, e non portatile.

Per esempio,

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

Stai solo andando con il C-Style, così la seconda linea è in realtà pari a:

double* z = (double*)&x; 

Ho appena odio il C-Style in fase di lancio, perché non si può dire lo scopo del cast da uno sguardo :)


In quali circostanze è garantito il funzionamento di ?

Questo non è reale fusione tra i tipi.Ad esempio,

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

Ora f punta allo stesso posto che i punti. Quindi, nessuna conversione è stata eseguita. Quando si denota f, si otterrà un float con la stessa rappresentazione binaria dell'intero i. Sono quattro byte sulla mia macchina.

+0

reinterpret_cast sarebbe una cattiva idea, poiché tecnicamente non è garantito il risultato di un puntatore allo stesso indirizzo. static_cast (static_cast ()) garantisce un puntatore allo stesso indirizzo. – jalf

+0

Grazie per la correzione. – Jann

2

Se due strutture POD iniziano con la stessa sequenza di membri, lo standard garantisce che sarete in grado di accedervi liberamente tramite un'unione. È possibile memorizzare A :: Point3D in un'unione e quindi leggere dal membro B :: Point3D, a condizione che si tocchino solo i membri che fanno parte della sequenza comune iniziale. (quindi se una struct conteneva int, int, int, float e l'altra contenuta int, int, int, int, sarà possibile accedere solo ai tre primi valori).

Quindi sembra un modo garantito in cui il codice dovrebbe funzionare. Significa anche il cast dovrebbe funzionare, ma non sono sicuro se questo è indicato esplicitamente nello standard.

Naturalmente tutto ciò presuppone che entrambe le strutture siano compilate con lo stesso compilatore per garantire l'ABI identico.

+0

+1: punto eccellente. Sarebbe un compilatore molto strano che ha deciso di trattare POD-struct in modo diverso quando è annidato in un sindacato oppure no. –

0

Quello che segue è abbastanza sicuro:

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