type para libreria example enum c++ casting enums

para - Manera genérica de convertir int a enum en C++



libreria para enum c++ (9)

¿Qué piensas acerca de esto?

#include <iostream> #include <stdexcept> #include <set> #include <string> using namespace std; template<typename T> class Enum { public: static void insert(int value) { _set.insert(value); } static T buildFrom(int value) { if (_set.find(value) != _set.end()) { T retval; retval.assign(value); return retval; } throw std::runtime_error("unexpected value"); } operator int() const { return _value; } private: void assign(int value) { _value = value; } int _value; static std::set<int> _set; }; template<typename T> std::set<int> Enum<T>::_set; class Apples: public Enum<Apples> {}; class Oranges: public Enum<Oranges> {}; class Proxy { public: Proxy(int value): _value(value) {} template<typename T> operator T() { T theEnum; return theEnum.buildFrom(_value); } int _value; }; Proxy convert(int value) { return Proxy(value); } int main() { Apples::insert(4); Apples::insert(8); Apples a = convert(4); // works std::cout << a << std::endl; // prints 4 try { Apples b = convert(9); // throws } catch (std::exception const& e) { std::cout << e.what() << std::endl; // prints "unexpected value" } try { Oranges b = convert(4); // also throws } catch (std::exception const& e) { std::cout << e.what() << std::endl; // prints "unexpected value" } }

Entonces podría usar el código que publiqué here para activar los valores.

¿Hay una forma genérica de convertir int a enum en C++ ?

Si int cae dentro del rango de una enum , debe devolver un valor enum ; de lo contrario, arroja una exception . ¿Hay alguna manera de escribirlo genéricamente ? Se debe enum type más de un enum type .

Antecedentes: tengo un tipo de enumeración externo y no tengo control sobre el código fuente. Me gustaría almacenar este valor en una base de datos y recuperarlo.


C ++ 0x alternativa a la versión "fea", permite múltiples enumeraciones. Utiliza listas de inicializadores en lugar de modificadores, un IMO un poco más limpio. Desafortunadamente, esto no funciona en torno a la necesidad de codificar los valores enum.

#include <cassert> // assert namespace // unnamed namespace { enum class e1 { value_1 = 1, value_2 = 2 }; enum class e2 { value_3 = 3, value_4 = 4 }; template <typename T> int valid_enum( const int val, const T& vec ) { for ( const auto item : vec ) if ( static_cast<int>( item ) == val ) return val; throw std::exception( "invalid enum value!" ); // throw something useful here } // valid_enum } // ns int main() { // generate list of valid values const auto e1_valid_values = { e1::value_1, e1::value_2 }; const auto e2_valid_values = { e2::value_3, e2::value_4 }; auto result1 = static_cast<e1>( valid_enum( 1, e1_valid_values ) ); assert( result1 == e1::value_1 ); auto result2 = static_cast<e2>( valid_enum( 3, e2_valid_values ) ); assert( result2 == e2::value_3 ); // test throw on invalid value try { auto result3 = static_cast<e1>( valid_enum( 9999999, e1_valid_values ) ); assert( false ); } catch ( ... ) { assert( true ); } }


Feo.

enum MyEnum { one = 1, two = 2 }; MyEnum to_enum(int n) { switch( n ) { case 1 : return one; case 2 : return two; } throw something(); }

Ahora para la verdadera pregunta. ¿Por qué necesitas esto? El código es feo, no es fácil de escribir (*?) Y no es fácil de mantener, y no es fácil de incorporar en tu código. El código te dice que está mal. ¿Por qué pelear?

EDITAR:

Alternativamente, dado que las enumeraciones son tipos integrales en C ++:

enum my_enum_val = static_cast<MyEnum>(my_int_val);

pero esto es incluso más feo que el anterior, mucho más propenso a los errores, y no arrojará como lo desee.


Lo obvio es anotar tu enum:

// generic code #include <algorithm> template <typename T> struct enum_traits {}; template<typename T, size_t N> T *endof(T (&ra)[N]) { return ra + N; } template<typename T, typename ValType> T check(ValType v) { typedef enum_traits<T> traits; const T *first = traits::enumerators; const T *last = endof(traits::enumerators); if (traits::sorted) { // probably premature optimization if (std::binary_search(first, last, v)) return T(v); } else if (std::find(first, last, v) != last) { return T(v); } throw "exception"; } // "enhanced" definition of enum enum e { x = 1, y = 4, z = 10, }; template<> struct enum_traits<e> { static const e enumerators[]; static const bool sorted = true; }; // must appear in only one TU, // so if the above is in a header then it will need the array size const e enum_traits<e>::enumerators[] = {x, y, z}; // usage int main() { e good = check<e>(1); e bad = check<e>(2); }

Necesitas que la matriz se mantenga actualizada con e , lo cual es una molestia si no eres el autor de e . Como dice Sjoerd, probablemente se pueda automatizar con cualquier sistema de construcción decente.

En cualquier caso, te enfrentas a 7.2 / 6:

Para una enumeración donde emin es el enumerador más pequeño y emax es el más grande, los valores de la enumeración son los valores del tipo subyacente en el rango bmin a bmax, donde bmin y bmax son, respectivamente, los valores más pequeños y más grandes de los más pequeños campo de bits que puede almacenar emin y emax. Es posible definir una enumeración que tiene valores no definidos por ninguno de sus enumeradores.

Entonces, si usted no es el autor de e , puede o no tener una garantía de que los valores válidos de e realmente aparecen en su definición.


No debería querer que exista algo como lo que describe, temo que haya problemas en el diseño de su código.

Además, supone que las enumeraciones están en un rango, pero ese no es siempre el caso:

enum Flags { one = 1, two = 2, four = 4, eigh = 8, big = 2000000000 };

Esto no está en un rango: incluso si fuera posible, ¿se supone que debes verificar cada entero de 0 a 2 ^ n para ver si coinciden con el valor de alguna enumeración?


No, no hay introspección en C ++, ni hay ninguna función incorporada en "verificación de dominio".


Pruebe algo como esto:

enum EType { type1, type2 }; unsigned int number = 3; EType e = static_cast<EType>(number); if(static_cast<unsigned int>(e) != number) throw std::exception();


Si está preparado para enumerar sus valores enum como parámetros de plantilla, puede hacerlo en C ++ 11 con plantillas varádicas. Puedes ver esto como algo bueno, permitiéndote aceptar subconjuntos de los valores enum válidos en diferentes contextos; a menudo útil al analizar códigos de fuentes externas.

Tal vez no sea tan genérico como quisieras, pero el código de comprobación en sí es generalizado, solo necesitas especificar el conjunto de valores. Este enfoque maneja vacíos, valores arbitrarios, etc.

template<typename EnumType, EnumType... Values> class EnumCheck; template<typename EnumType> class EnumCheck<EnumType> { public: template<typename IntType> static bool constexpr is_value(IntType) { return false; } }; template<typename EnumType, EnumType V, EnumType... Next> class EnumCheck<EnumType, V, Next...> : private EnumCheck<EnumType, Next...> { using super = EnumCheck<EnumType, Next...>; public: template<typename IntType> static bool constexpr is_value(IntType v) { return v == static_cast<typename std::underlying_type<EnumType>::type>(V) || super::is_value(v); } EnumType convert(IntType v) { if (!is_value(v)) throw std::runtime_error("Enum value out of range"); return static_cast<EnumType>(v); }; enum class Test { A = 1, C = 3, E = 5 }; using TestCheck = EnumCheck<Test, Test::A, Test::C, Test::E>; void check_value(int v) { if (TestCheck::is_value(v)) printf("%d is OK/n", v); else printf("%d is not OK/n", v); } int main() { for (int i = 0; i < 10; ++i) check_value(i); }


Si, como describes, los valores están en una base de datos, ¿por qué no escribir un generador de códigos que lea esta tabla y cree un archivo .h y .cpp con las funciones enum y to_enum(int) ?

Ventajas:

  • Fácil de agregar una función to_string(my_enum) .
  • Poco mantenimiento requerido
  • La base de datos y el código están sincronizados