2012-10-10 17 views
14

Ho la necessità di utilizzare offsetof da un template con un selettore membro. Mi è venuta in mente un modo, se volete scusarmi la sintassi imbarazzante:C++ Compile-Time offsetof all'interno di un modello

template <typename T, 
      typename R, 
      R T::*M 
     > 
constexpr std::size_t offset_of() 
{ 
    return reinterpret_cast<std::size_t>(&(((T*)0)->*M)); 
}; 

uso non è perfetto (fastidioso nella migliore delle ipotesi):

struct S 
{ 
    int x; 
    int y; 
}; 

static_assert(offset_of<S, int, &S::x>() == 0, ""); 
static_assert(offset_of<S, int, &S::y>() == sizeof(int), ""); 

Il constexpr forma non è più facile da usare:

template <typename T, typename R> 
std::size_t offset_of(R T::*M) 
{ 
    return reinterpret_cast<std::size_t>(&(((T*)0)->*M)); 
}; 

allo svantaggio ovvio che non è fatto a tempo di compilazione (ma più facile da usare):

int main() 
{ 
    std::cout << offset_of(&S::x) << std::endl; 
    std::cout << offset_of(&S::y) << std::endl; 
} 

Quello che sto cercando è la sintassi come la varietà non constexpr, ma ancora completamente in fase di compilazione; tuttavia, non riesco a trovare la sintassi per questo. Sarei anche contento di un offset_of<&S::x>::value (come il resto dei tratti del tipo), ma non riesco a capire la magia della sintassi per questo.

+0

Sto cercando di capire dove nello standard si dice che questo fa quello che ti aspetti che faccia. Ma non riesco a trovarlo. –

+4

Cosa c'è di sbagliato con lo standard ['offsetof'] (http://en.cppreference.com/w/cpp/types/offsetof)? –

+0

@NicolBolas Immagino che non sia così. Non dovrebbe esserci già UB il dereferenziamento di un 'nullptr' (e penso che' -> 'valga come dereferenziazione)? Ma poi di nuovo, la versione di VC della macro 'offsetof' non è diversa. Quindi in pratica è probabilmente l'implementazione definita piuttosto che non definita. –

risposta

13

Il seguente dovrebbe funzionare (crediti vanno a the answer to this question per l'idea):

#include <cstddef> 

template <typename T, typename M> M get_member_type(M T::*); 
template <typename T, typename M> T get_class_type(M T::*); 

template <typename T, 
      typename R, 
      R T::*M 
     > 
constexpr std::size_t offset_of() 
{ 
    return reinterpret_cast<std::size_t>(&(((T*)0)->*M)); 
} 

#define OFFSET_OF(m) offset_of<decltype(get_class_type(m)), \ 
        decltype(get_member_type(m)), m>() 

struct S 
{ 
    int x; 
    int y; 
}; 

static_assert(OFFSET_OF(&S::x) == 0, ""); 

Si noti che in gcc, i offsetof macro restituisce un'estensione incorporato che può essere utilizzato al momento della compilazione (vedi sotto). Inoltre, il tuo codice richiama UB, denota un puntatore nullo, quindi anche se potrebbe funzionare nella pratica, non ci sono garanzie.

#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER) 

Come sottolineato da Luc Danton, espressioni costanti non possono comportare un reinterpret_cast secondo lo standard C++ 11 benché attualmente gcc accetta il codice (vedere la bug report here). Inoltre, ho trovato defect report 1384 che parla di rendere le regole meno severe, quindi questo potrebbe cambiare in futuro.

+0

Mi sembra buono, quindi. – hvd

+4

Un'espressione costante non può includere un 'reinterpret_cast' (se non valutato). –

+1

@LucDanton: Grazie per le informazioni. Ho anche trovato un [rapporto sui difetti 1384] (http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1384) che parla di allentare le restrizioni a riguardo. –

Problemi correlati