2012-06-22 3 views
6

c'è un modo, al momento della compilazione, per verificare che un dato valore è entro i valori di un dato enum, così valida?C++ 11 metaprogrammazione - lookup valore enum durante la compilazione (valori contiene lacune)

enum myenum { val1 = 10, val2 = 30, val3 = 45 } 

template <myenum t> 
class myClass 
{ ... } 

myClass<10> a; // fails, OK 
myClass<val1> b; // compiles OK 
myClass<myenum(24)> c; //compiles, NOT OK! 

Utilizzando un non-tipo di parametro booleano secondo modello sarebbe utile lì, e il valore di tale booleano sarebbe dato da una meta-funzione, che dato un valore potrebbe verificare che il valore rientra nei valori di myenum.

Ho esaminato varie domande relative all'enumerazione, ad esempio come iterare un'enumerazione e sembra che non possa essere eseguita.

+0

Con un cast di tipo 'C', non penso che sia possibile impedirlo. – Chad

+0

'myenum (24)' è ancora un valore valido di quel tipo. Potresti non voler gestire tale valore, ma è comunque valido. Nei tipi di enumerazione C e C++ non sono limitati ai valori che corrispondono agli enumeratori, che è di progettazione ed è una buona cosa. –

+0

@JonathanWakely Penso che non sia una buona cosa * non avere l'altra opzione *. –

risposta

0

Non è possibile eseguire la redizione di tutte le informazioni in formato metaprogrammazione. Tuttavia, si potrebbe per esempio fare:

enum myenum { val1 = 10, val2 = 30, val3 = 45 }; 
typedef vector_c< myenum, val1, val2, val3 > typed_myenum; 

typedef 
    contains< typed_myenum, integral_c< myenum, val >::value 
    val_found; 

Utilizzando la funzionalità di Boost.MPL, anche se la sintassi effettivo potrebbe essere un po 'fuori.

2

Non si può mai impedire agli altri sviluppatori di sparare con i propri piedi. Non puoi mai vincere. Assicurati che la tua API non lo rende facile.

Se prende un valore non valido, lo lancia, e poi lo passa, è puramente un suo problema. Questo è lo stesso tipo di problema che se gli fornisci una funzione prendendo un puntatore di tipo T* come parametro, ma prende alcuni dati casuali (di tipo arbitrario), li trasmette a T* e li inoltra.

Ecco come lavori di casting: la persona che fa il cast è responsabile di garantire che l'espressione casted possa essere tranquillamente interpretata come valore del tipo a cui si rivolge.

+0

Beh, tecnicamente, in questo caso, posso, e se riesco a rendere molto difficile l'abuso, preferirei farlo. Ciò è possibile con la specializzazione del modello o con l'istatiazione esplicita del modello. Ma in entrambi i casi, se cambio l'enumeratore, devo assicurarmi che la specializzazione o l'instatiazione esplicita sia sincronizzata e non mi piace. – emitrax

+0

@emitrax: rimuoverete un proiettile da molti, aggiungendone potenzialmente altri digitando un sacco di codice non necessario in modo che altri peeople possano ancora sparare ai loro piedi. E probabilmente lo renderà meno leggibile. Non lo considero un buon compromesso, ma la tua scelta. – mity

+0

Per citare come tutte le principali domande frequenti su C++: "NON cercare di impedire lo spionaggio. –

5
enum myenum { val1 = 10, val2 = 30, val3 = 45 }; 
template<myenum e> struct is_valid_myenum { static const bool value = (e==val1 || e==val2 || e==val3); }; 

template<myenum t> 
class myClass 
{ 
    static_assert(is_valid_myenum<t>::value, "t must be a valid enum value"); 
}; 

myClass<10> a; // fails, OK 
myClass<val1> b; // compiles OK 
myClass<myenum(24)> c; // fails, OK 

Se davvero, vuole veramente evitare la duplicazione (e non siete interessati a utilizzare qualche strumento esterno per generare codice sorgente) si può ricorrere ad aggiustamenti macro.

#define LIST \ 
    ITEM(val1,10)\ 
    ITEM(val2,30)\ 
    ITEM(val3,45) 

#define ITEM(NAME,VALUE) NAME = VALUE, 

enum myenum { LIST }; 

#undef ITEM 

#define ITEM(NAME,VALUE) e==NAME || 

template<myenum e> struct is_valid_myenum { static const bool value = (LIST false); }; 

template<myenum t> 
class myClass 
{ 
    static_assert(is_valid_myenum<t>::value, "t must be a valid enum value"); 
}; 

myClass<10> a; // fails, OK 
myClass<val1> b; // compiles OK 
myClass<myenum(24)> c; // fails, OK 
+0

Questa è una soluzione interessante. Grazie! Tuttavia, non risponde alla mia domanda. Il problema con questa soluzione è che se cambio i valori enum (aggiungi o cancella valori) devo assicurarmi che is_valid_myenum sia aggiornato, e che sia soggetto a errori. Ecco perché volevo un modo per scorrere il valore di enumerazione. – emitrax

0

ci sono modi per controllare che un valore è all'interno di un intervallo valido di un enum in fase di esecuzione, meno al momento della compilazione.

L'unico modo per controllare è il controllo della forza bruta per ogni valore. Qualcosa di simile a questo:

enum lala 
{ 
    A = 10, 
    B = 20, 
    C = 30 
}; 

template< int value > 
struct T 
{ 
    static_assert((value == A) || (value == B) || (value == C), "wrong value"); 
}; 

int main() 
{ 
    T<10> t10; 
    T<20> t20; 
    T<25> t25; 
    T<30> t30; 

    (void)t10;(void)t20;(void)t25;(void)t30; 
} 
0

Se il nome del enum non è importante per voi, forse consentire di trasferire a un array, come ad esempio:

const int myenum[]={10,30,45} 

quindi utilizzare un indice a loro riferimento dalla MyEnum.

template<unsigned int t> 
class myClass 
{ 
    static_assert(t < sizeof(myenum)/sizeof(int), "t must be a valid enum index"); 
    int val=myenum[t]; 
    ... 
};