CashCow presenta a decent answer a questa domanda: è certamente semplice scrivere una funzione personalizzata per eseguire un cast controllato.
Purtroppo, è anche un sacco di lavoro e si deve fare in modo di mantenerlo sincronizzato con l'enumerazione in modo che l'elenco dei enumeratori nella definizione enumerazione è la stessa della lista di enumeratori nella funzione getto controllato. Devi anche scrivere uno di questi per ogni enumerazione a cui vuoi essere in grado di eseguire un cast controllato.
Invece di fare tutto questo lavoro manuale, possiamo automatizzare la generazione di tutto questo codice usando il preprocessore (con un piccolo aiuto dalla libreria Boost Preprocessor). Ecco una macro che genera una definizione di enumerazione insieme a una funzione checked_enum_cast
. Probabilmente è un po 'spaventoso (le macro di generazione del codice sono spesso orribili da considerare), ma è una tecnica estremamente utile con cui familiarizzare.
#include <stdexcept>
#include <boost/preprocessor.hpp>
// Internal helper to provide partial specialization for checked_enum_cast
template <typename Target, typename Source>
struct checked_enum_cast_impl;
// Exception thrown by checked_enum_cast on cast failure
struct invalid_enum_cast : std::out_of_range
{
invalid_enum_cast(const char* s)
: std::out_of_range(s) { }
};
// Checked cast function
template <typename Target, typename Source>
Target checked_enum_cast(Source s)
{
return checked_enum_cast_impl<Target, Source>::do_cast(s);
}
// Internal helper to help declare case labels in the checked cast function
#define X_DEFINE_SAFE_CAST_CASE(r, data, elem) case elem:
// Macro to define an enum with a checked cast function. name is the name of
// the enumeration to be defined and enumerators is the preprocessing sequence
// of enumerators to be defined. See the usage example below.
#define DEFINE_SAFE_CAST_ENUM(name, enumerators) \
enum name \
{ \
BOOST_PP_SEQ_ENUM(enumerators) \
}; \
\
template <typename Source> \
struct checked_enum_cast_impl<name, Source> \
{ \
static name do_cast(Source s) \
{ \
switch (s) \
{ \
BOOST_PP_SEQ_FOR_EACH(X_DEFINE_SAFE_CAST_CASE, 0, enumerators) \
return static_cast<name>(s); \
default: \
throw invalid_enum_cast(BOOST_PP_STRINGIZE(name)); \
} \
return name(); \
} \
};
Ecco come si usa che con il vostro CardColor
esempio:
DEFINE_SAFE_CAST_ENUM(CardColor, (HEARTS) (CLUBS) (SPADES) (DIAMONDS))
int main()
{
checked_enum_cast<CardColor>(1); // ok
checked_enum_cast<CardColor>(400); // o noez! an exception!
}
La prima riga sostituisce la tua definizione enum CardColor ...
; definisce l'enumerazione e fornisce una specializzazione che consente di utilizzare checked_enum_cast
per eseguire il cast degli interi su CardColor
.
Questo può sembrare un sacco di problemi solo per ottenere una funzione di cast controllata per l'enumerazione, ma questa tecnica è molto utile ed estensibile. Puoi aggiungere funzioni che fanno ogni sorta di cose. Ad esempio, ne ho uno che genera funzioni per convertire tipi enumerati in e da rappresentazioni di stringa e funzioni che eseguono diverse altre conversioni e controlli che utilizzo per la maggior parte delle mie enumerazioni.
Ricordare, è necessario scrivere e eseguire il debug di quella macro grande e brutta solo una volta, quindi è possibile utilizzarla ovunque.
In fase di runtime o in fase di compilazione? – CashCow
Oltre a "non farlo"? –
@CashCow: bene, entrambi! Ho aggiornato la mia domanda. –