2012-06-17 10 views
13

Ho trovato una soluzione semplice da qualche parte su Internet a una classe di identità senza C + + RTTI incorporato.Identità di classe senza RTTI

template <typename T> 
class Identity { 
public: 
    static int64_t id() 
    { 
     static int64_t dummy; 
     return reinterpret_cast<int64_t>(&dummy); 
    } 
}; 

Quando abbiamo bisogno di un po 'di ID di classe, usiamo:

Identity<OurClass>::id(); 

mi chiedo, ci sono collisioni? Può restituire lo stesso ID per le diverse classi o il diverso ID per le stesse classi? Ho provato questo codice con g ++ con diversi valori di ottimizzazione, tutto sembra ok.

+0

In linea di principio, sì. Non c'è alcuna garanzia che un puntatore a funzione abbia le stesse dimensioni di un 'int'. –

+1

Questo è rilevante per i miei interessi ... –

+0

Per evitare il problema precedente, sarebbe meglio inserire una variabile 'int' statica in quel template di funzione membro statico e restituire un puntatore a * that *. Il compilatore ottimizzerà comunque la funzione. – Electro

risposta

12

Innanzitutto: è un tipo intero tale che è fatto appositamente per contenere i puntatori:

  • intptr_t
  • e in C++ 11 uintptr_t

secondo luogo, anche se in pratica su gcc sono uguali, le dimensioni di un puntatore a un oggetto e le dimensioni di un puntatore a funzione (o puntatore a membro) potrebbero essere diverse. Quindi sarebbe meglio usare un oggetto specifico piuttosto che il metodo stesso (per la conformità Standard).

In terzo luogo, fornisce solo l'identità, mentre RTTI è molto più ricco, in quanto conosce tutte le sottoclassi a cui è possibile eseguire il cast di un determinato oggetto e consente persino cross-cast o cast attraverso l'ereditarietà virtuale.

Eppure, la versione corretta può essere utile immagino:

struct Foo { 
    static intptr_t Id() { 
     static boost::none_t const Dummy = {}; 
     return reinterpret_cast<intptr_t>(&Dummy); 
    } 
}; 

E nelle gerarchie, con una funzione virtual ritorno che ID.

Per completezza, menzionerò che Clang e LLVM hanno il loro modo di trattare l'identificazione degli oggetti senza RTTI. Si consiglia di leggere il loro modo di implementare isa, cast e dyn_casthere.

+0

Upvoted per spiegazione completa. – Electro

+0

grazie, ma non ho ottenuto, perché le dimensioni di un puntatore a un oggetto e le dimensioni di un puntatore a funzione potrebbero essere diverse? Puoi fare un esempio? – pproger

+0

@pproger: le dimensioni dei puntatori di funzione dei membri virtuali spesso differiscono per dimensioni dai puntatori regolari. Questo è comunque specifico del compilatore, puoi leggere ulteriori informazioni in questo eccellente articolo sul codeproject: http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible –

-1

Questa soluzione lancia un puntatore a un int. Non vi è alcuna garanzia che questo puntatore si adatti a un , sebbene nella pratica sizeof(void *) == sizeof(void (*)()) <= sizeof(int)

Modifica: Il mio male. Su x86_64 sizeof(int) = 4, sizeof(void (*)()) = 8, quindi le collisioni sono possibili e sono imprevedibili.

È possibile eseguire il cast su un integrale di dimensioni appropriate, ma è ancora un comportamento non definito teoricamente.

+0

Questo non è sempre vero nella pratica; prova su x86-64. –

+0

ok, ho modificato il codice. e adesso? – pproger

0

Questa versione evita comportamento non definito (e avvisi del compilatore):

template <typename T> 
class Identity { 
public: 
    static const int* id() { static const int id = 0; return &id; } 
}; 
+0

Non ho bisogno del valore di ritorno come puntatore. voglio int :) – pproger

+0

Perché ti importa se è un puntatore o no? – Electro

+0

non. autore del codice) – pproger