2011-04-04 9 views
7

Diciamo che ho questo tipo:Come fare una affermazione statica che un cast puntatore è banale?

struct A { 
    int a; 
}; 

struct B { 
    int b; 
}; 

struct C : public A, public B { 
    int c; 
}; 

Un puntatore C* può essere lanciato a A* puntatore senza regolare l'indirizzo effettivo a tutti. Tuttavia, quando viene eseguito il cast dello C* su B*, il valore deve essere modificato. Mi piacerebbe assicurarmi che due tipi correlati che ho possano essere espressi l'uno con l'altro senza modificare l'indirizzo (cioè che non ci sia ereditarietà multipla o che la classe base sia la prima base della classe derivata). Questo potrebbe essere verificato in fase di esecuzione, ad es. in questo modo

assert(size_t(static_cast<A*>((C*)0xF000) == 0xF000); 
assert(size_t(static_cast<B*>((C*)0xF000) != 0xF000); 

che funziona. Ma questa informazione è nota al momento della compilazione, quindi sto cercando un modo per fare una dichiarazione in fase di compilazione su di esso. I modi ovvi conversione dell'anticipo di cui sopra per un assert statico (ad esempio sostituire assert con BOOST_STATIC_ASSERT dare l'errore "un getto di un tipo diverso da un tipo intero o di enumerazione non può apparire in una costante espressione" con g ++ 4.2.

Mobilità non è troppo importante Uso delle estensioni gcc, o trucchi modello hacky sarebbe tutto bene

Aggiornamento:.. trovato che quasi la stessa domanda è stato chiesto prima:. C++, statically detect base classes with differing addresses? Utilizzando offsetof() è l'unico suggerimento utile anche lì.

+8

Non voler essere un nitpick, ma dal momento che l'esatta configurazione della memoria è un dettaglio di implementazione ... perché ti interesserebbe esattamente? –

+0

Sono abbastanza sicuro che le librerie Boost/lambda contengano cose per questo. Dovrei cercare, ma Alexandrescu e Vandervoorde hanno pubblicato quasi tutti i trucchi in questo campo – sehe

+0

Sono davvero interessato alla domanda che @Matthieu M. presenta: In base a quale circostanza importa se un cast può essere banale? (cioè se non è banale, il costo è probabilmente trascurabile: aggiunta di una costante al puntatore derivato e se la condizione non viene eseguita correttamente si verificherà un vero dolore cercando di eseguire il debug di ciò che apparirà come memoria corrotta) –

risposta

1

Sulla base di un suggerimento di MSalters e una risposta da C++, statically detect base classes with differing addresses?, ecco la cosa più vicina a una risposta che riesco a trovare. E 'probabilmente specifici per gcc, e richiede sapendo alcuni membro della classe base:

#pragma GCC diagnostic ignored "-Winvalid-offsetof"  // To suppress warning. 
BOOST_STATIC_ASSERT(offsetof(C, a) == offsetof(A, a)); 
BOOST_STATIC_ASSERT(offsetof(C, b) != offsetof(B, b)); 
#pragma GCC diagnostic warn "-Winvalid-offsetof" 

Ovviamente questo è sia scomodo e spaventoso (richiede di conoscere un membro e per spegnere un avvertimento).

4

"Mi piacerebbe assicurare che due relativi i tipi possono essere espressi l'un l'altro senza modificare l'indirizzo (ad es. che non c'è ereditarietà multipla, o che la classe di base è la prima base della classe derivata)."

Your 'cioè' non è corretto. Ad esempio, è del tutto possibile che i primi 4 byte di Derived sono un puntatore vtable, anche quando Base non è polimorfico Sì, i compilatori C++ sono più facili se (1) il primo subobject di base è all'offset 0 e (2) il puntatore vtable è all'offset 0. Ma questi due obiettivi sono intrinsecamente in le probabilità, e non c'è scelta chiara meglio.

Ora, la prima parte potrebbe essere testati in teoria. Con i tipi standard di layout, non ci sarebbe alcuna differenza di offset_of(Base, first_member) e offset_of(Derived, first_member). Ma in pratica, offset_of non funziona per tipi interessanti, è ' s UB. E l'intero punto di questo controllo è controllare un tipo, quindi dovrebbe fallire in modo affidabile per i tipi di layout non standard.

+1

"Non è possibile verificarlo perché non è vero". Quello che intendevo è che, poiché non è sempre vero, devo essere in grado di controllare! Ora, l'idea di offset di (C, a) sembrava molto promettente, ma gcc si lamenta di "Accesso non valido al membro di dati non statici" A :: a "dell'oggetto NULL." –

+0

In realtà, il reclamo di gcc è un avvertimento, non un errore. Quindi funziona quando è noto il primo membro di Base. È qualcosa. Grazie! –

+0

Ho modificato il commento di MSalters "Non puoi controllare che, perché non è vero" all'I-think-clearer "Il tuo 'i.e." non è corretto ". Non penso che MSalters avrebbe potuto tentare di dire che tutta la sua affermazione era sbagliata. Sulla base della sua prossima frase, penso che volesse dire che "due tipi correlati possono essere lanciati ..." non è in realtà equivalente a "non c'è eredità multipla ...". – Quuxplusone

Problemi correlati