2015-12-11 10 views
8

Il typeid consente di assegnare un unico std::type_index a ciascun tipo in fase di esecuzione. Vorrei fare lo stesso, in modo statico utilizzando due METAFUNCTIONS:Metafunzione per convertire un tipo in un numero intero e viceversa

// Get a unique integral number associated with the provided type 
template <class T> 
struct encode_type 
{ 
    using type = T; 
    static constexpr std::size_t value = /* Metaprogramming magic */; 
}; 

// Get the type uniquely associated with the provided value 
template <std::size_t V> 
struct decode_type 
{ 
    static constexpr std::size_t value = V; 
    using type = /* Metaprogramming magic */; 
}; 

C'è un modo per farlo in C++ 11?

+2

Perché lo vorrebbe, dal momento che in fase di compilazione hai ottenuto il tipo stesso? – 101010

+0

Escludendo alcuni hack malvagi, non penso che il conteggio (o la generazione di altri interi unici) al momento della compilazione come questo sia possibile, * a meno che * non siate disposti a enumerare tutti i tipi per i quali dovrebbe funzionare ('tuple ' , quindi utilizzare l'indice). – melak47

+1

Come ho sottolineato di seguito, la risposta è "no". Tuttavia, è probabile che tu stia chiedendo * più di quanto tu non abbia realmente bisogno *. Le probabilità sono che hai un'applicazione in mente, ed è proprio quello che vuoi provare. Ma non hai incluso la tua motivazione generale in quanto sopra. Sentiti libero di fare una domanda che include ciò che vuoi realmente fare in un'altra domanda! – Yakk

risposta

8

Ecco una possibile soluzione che "funziona" con GCC 5.2 e Clang 3.7.

Io uso Filip Roséen 's Constexpr Meta-Container con alcune lievi modifiche. Come T.C. sottolineato, questo potrebbe essere made ill-formed in futuro, quindi questa soluzione è totalmente irragionevole nel codice di produzione, ma per ora è abbastanza interessante. Non sono nemmeno sicuro se questo è conforme al 100% agli standard.

// This is our meta-container 
using TypeMap = atch::meta_list<class A>; 

// Get a unique integral number associated with the provided type 
template <class T> 
struct encode_type 
{ 
    using type = T; 
    // Push T into the container and store the pre-push size 
    //(requires slight change to Filip's code) 
    static constexpr std::size_t value = TypeMap::push<T>(); 
}; 

// Get the type uniquely associated with the provided value 
template <std::size_t V> 
struct decode_type 
{ 
    static constexpr std::size_t value = V; 
    // Get the type at index V 
    // (requires a small helper function addition) 
    using type = decltype(TypeMap::at<V>()); 
}; 

Le modifiche apportate ai codice originale:

template<class T, class H = meta_list, std::size_t Size = counter::value()> 
static constexpr std::size_t push (
    size_type = push_state< 
    typename H::template value<>::template push<T>::result 
    >() 
) { return Size; } 

I modified atch::meta_list::push per restituire la dimensione del meta-contenitore prima la spinta. Ho usato un parametro template con un argomento predefinito per garantire che la dimensione sia calcolata prima della push.

template<size_type Idx, class H = meta_list> 
static constexpr auto at() -> typename H::template value<>::template at<Idx>::result; 

ho aggiunto una piccola funzione decltype aiuto nella atch::meta_list al fine di nascondere tutto quel nome pasticcio dipendente.


Alcuni codice di prova:

int main() { 
    std::array<int, 4> encoded { 
     encode_type<int>::value, 
     encode_type<double>::value, 
     encode_type<std::string>::value, 
     encode_type<float>::value 
    }; 

    std::cout << "Encoding: "; 
    for (auto i : encoded) std::cout << i << ", "; 
    std::cout << std::endl; 

    std::array<std::type_index, 4> decoded { 
     typeid(decode_type<0>::type), 
     typeid(decode_type<1>::type), 
     typeid(decode_type<2>::type), 
     typeid(decode_type<3>::type), 
    }; 

    std::cout << "Decoding: "; 
    for (auto i : decoded) std::cout << i.name() << ", "; 
    std::cout << std::endl; 
} 

Sia Clang e GCC vomitare un sacco di avvertimenti, ma entrambi "lavoro"!

Clang compiles, runs and outputs:

Codifica: 0, 1, 2, 3,

decodifica: I, D, NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE, f,

GCC compiles, runs and outputs:

Codifica: 0, 1, 2, 3,

decodifica: I, D, NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE, f,


Forse sarebbe meglio con una fase di pre-processing ...

+0

Non sono sicuro che sia utile, ma chiaramente è divertente! Grazie! – YSC

+0

Gestisce più chiamate 'codecode_type '? (e non ricodifica un nuovo valore) – Yakk

+0

@ Yakk Sì, la codifica viene eseguita tramite l'istanziazione del modello, quindi le chiamate multiple con lo stesso argomento non sono opzionali. – TartanLlama

4

Esistono più tipi in C++ di quantificabili con size_t.

Come prova:

template<size_t N> struct bob {}; 

ci sono esattamente come molti tipi bob<X> quante sono le size_t valori.

template<class...> struct alice {}; 

ora, alice<bob<1>, bob<2>> è un tipo valido. Quindi è alice<alice<bob<1>>.

Ci sono molti altri tipi alice rispetto ai tipi bob.

Quindi non è stata eseguita l'iniezione dal set di valori size_t all'insieme di tipi.

Pertanto qualsiasi mappatura di questo tipo deve essere incompleta se biiettiva. È possibile mappare solo alcuni sottoinsiemi di tipi a size_t e viceversa.

Non hai specificato di voler gestire solo un sottoinsieme di tipi, quindi la risposta è, non puoi fare ciò che hai chiesto.

Si noti che std::type_index è semplicemente un oggetto confrontabile e lavabile. Non è un "indice" come un valore intero.

+0

Questo argomento è irrilevante. Qualsiasi programma finito crea solo un'istanza di un numero finito di tipi. Non è necessario che i valori interi assegnati siano coerenti tra i programmi. Quindi per ogni singolo programma non esiste alcun ostacolo relativo all'infinito alla creazione di una mappa da tipi a interi come richiesto dall'interrogante. – eh9

+0

@ eh9 Posso istanziare più di tipi 'size_t' in un singolo programma. Sarebbe un grande programma su un sistema con un piccolo 'size_t', ma non è * difficile *. Il presupposto che l'OP voglia solo limitarlo ai tipi dichiarati nel programma corrente, o all'unità di compilazione corrente, o solo i tipi che vengono passati a 'encode_type' o 'decode_type's istanziati nell'unità di compilazione corrente, che è un diverso domanda rispetto a ciò che l'OP ha chiesto. – Yakk

+0

Angeli e teste di spillo. Suppongo che l'interrogante si accontenterebbe di una mappa per qualsiasi tipo di integrale e sarebbe soddisfatta anche con una limitazione della dimensione totale dei programmi. Non c'è nulla nella tua argomentazione che sia diverso dalla relazione ordinaria tra un linguaggio formale definito su input di lunghezza arbitraria e un'implementazione finita di quel linguaggio. L'implementazione è sempre un'approssimazione di alcuni sottoinsiemi di lunghezza finita. – eh9

0

Darò una soluzione parziale, poiché non sono interessato a elaborare tutti i dettagli disordinati. Utilizzare una macro che accetta un argomento contenente l'intera definizione del tipo. Ora fai tre cose con questo argomento (ci saranno macro helper):

  • Utilizzare l'argomento as-is per definire il tipo.
  • Passa l'argomento con stringhe a una funzione di hash crittografica.
    • L'output di tale funzione sarà un numero intero di un numero di bit. Non c'è dubbio perché è crittograficamente sicuro, che il numero intero sarà più grande dei tipi integrati integrati. Quindi invece di o size_t o quant'altro, usa il tuo tipo di indice; può essere un POD.
  • Il valore di tale funzione di hash è il valore magica per la definizione encode_type e decode_type.
    • A causa della limitazione sui tipi nell'argomento modello, potrebbe essere necessario utilizzare l'indirizzo di constexpr stoccaggio tenendo il valore hash per definire decode_type.

Il principio di base qui è lo stesso uso in una prova di Gödel del suo teorema di incompletezza, e cioè che una stringa di caratteri ha sempre due interpretazioni, prima come membro di un sistema formale e la seconda come numero. (Il nucleo della dimostrazione, per inciso, è di creare un modo di parlare di quel numero all'interno del sistema formale.)

I dettagli dell'utilizzo di strumenti prontamente disponibili in una catena di strumenti C++ sono disordinati, per essere sicuri. L'elemento chiave, tuttavia, che rende possibile l'operatore di stringificazione #. Questo è analogo alla seconda interpretazione precedente, quella della definizione non come membro di una lingua, ma come "solo dati".

Problemi correlati