Hay una forma genérica para lanzar int a enum en C++?

Si int cae en el rango de un enum debe devolver un enum valor, de lo contrario lanzar un exception. Es allí una manera de escribir genéricamente? Más de una enum type debe ser apoyada.

Antecedentes: tengo un externo enum tipo y ningún control sobre el código fuente. Me gustaría, este valor se almacena en una base de datos y recuperar.

  • enum e{x = 10000}; en este caso 9999 caen en el rango de la enum?
  • No, 9999 no se caiga.
  • Buena pregunta. Como para cualquier «¿por qué?», que va a aparecer, permítanme decir «deserialización» – parece suficiente razón para mí. También me alegra saber C++0x-compilant respuesta para enum class.
  • «Rango» es la palabra equivocada aquí, tal vez «dominio»?
  • boost::numeric_cast<> arroja un resultado positivo o negativo de la excepción de desbordamiento si el valor fuera de los límites. Pero no es seguro si se mantiene bien para tipos enum así. Usted puede probar que.
InformationsquelleAutor Leonid | 2010-11-12

8 Comentarios

  1. 37

    Lo que es obvio es anotar su enumeración:

    //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);
    }

    Necesita la matriz a la que se mantiene hasta la fecha con e, que es una molestia si no eres el autor de e. Como Sjoerd dice, lo que probablemente puede ser automatizado con cualquier decente sistema de construcción.

    En cualquier caso, usted está para arriba contra 7.2/6:

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

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

  2. 20

    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 a por la pregunta. ¿Por qué necesita esta? El código es feo, no es fácil escribir (*?) y no es fácil de mantener, y no es fácil de incorporar en el código. El código que decirle que lo que está mal. ¿Por qué luchar?

    EDICIÓN:

    Alternativamente, dado que las enumeraciones son parte integral de tipos en C++:

    enum my_enum_val = static_cast<MyEnum>(my_int_val);

    pero esto es aún más feo que el anterior, mucho más propenso a errores, y no va a tirar como usted desea.

    • esto sólo admite un tipo de MyEnum.
    • A mi conocimiento, no se puede hacer de forma genérica. En algún nivel, cualquier solución que usted viene para arriba con que se throw (o hacer algo especial) validez de los tipos deben tener un interruptor como las que he publicado.
    • Exactamente, reinterpret_cast no comprobar la validez de enum los valores, y es feo. Pero como los valores de enumeración son conocidos en tiempo de compilación tal vez hay una manera de hacerlo a través de plantillas, o tal vez no será una manera de hacer que en C++0x?
    • ¿Por qué esto es -1 ed? Es la respuesta correcta. Solo porque no es la respuesta que algunos estaban esperando no significa que sea malo.
    • Un static_cast<MyEnum> va a trabajar así, y debe ser preferida sobre la reinterpret_cast<MyEnum>
    • Estoy de acuerdo, y ha editado.
    • Este enfoque funciona bien, y aún mejor si se utiliza una herramienta para generar la función.
    • enum my_enum_val probablemente debería leer MyEnum my_enum_val, ¿verdad? Y el static_cast<MyEnum>(my_int_val) va a tirar si my_int_val está fuera de rango para el tipo de datos que MyEnum está utilizando internamente-no que eso es de mucha ayuda para el promedio de int

  3. 3

    Si, como usted describe, los valores están en una base de datos, ¿por qué no escribir un generador de código que lee esta tabla y crea una .h and .archivo cpp, tanto con la enumeración y una to_enum(int) función?

    Ventajas:

    • Fácil agregar un to_string(my_enum) función.
    • Poco mantenimiento requerido
    • De la base de datos y el código están en sintonía
    • No existe control de la enum escriba el código fuente. Estoy de acuerdo en que una instalación puede ser generado que implementa la conversión. Sin embargo, la instalación no ser conscientes de los cambios/extensiones externas enum tipo (a menos que se ejecuta cada vez que en tiempo de compilación).
    • Luego de leer que la enumeración de encabezado y generar el to_enum(int) función que se basa en eso.
    • Cada proyecto serio del sistema de gestión, incluso make, se puede comparar la fecha de los dos archivos para ver si el generador tiene que volver a ejecutar.
    • Creo que voy a ir con una solución más simple a la de un generador de ahora. Pero gracias por la idea.
    • Nosotros utilizamos este sistema en nuestro lugar de trabajo, una herramienta que genera .hpp código a partir de una plantilla, la plantilla es mínima.
  4. 2

    No hay ninguna introspección en C++, ni hay ninguna construido en el «dominio de verificación» de las instalaciones.

  5. 2

    ¿Qué piensa usted 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, usted puede utilizar el código que he publicado aquí a cambiar en valores.

    • Usted todavía necesita para agregar Apples::insert(4) en algún lugar, por lo que este no tiene ninguna ventaja sobre un interruptor.
    • Él dijo que los valores provienen de una base de datos, por eso he añadido un «insertar» método.
  6. 1

    Usted no debe querer algo parecido a lo que usted describe a existir, me temo que hay problemas en el código de diseño.

    También, se asume que las enumeraciones vienen en una amplia gama, pero eso no es siempre el caso:

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

    Esto no es en un rango de: incluso si era posible, se supone que hay que comprobar cada número entero de 0 a 2^n para ver si coinciden con algunos de enumeración del valor?

    • cómo podría recuperar en caso contrario los valores enum de la base de datos? Los enteros se conoce en tiempo de compilación, así que ¿por qué es que no es posible tener un genérico de conversión basados en plantillas?
    • Porque en algún nivel, usted necesita tener un interruptor, como he dicho.
    • Las plantillas no son una panacea para resolver todos los problemas que usted puede pensar.
    • Juan está a la derecha. Se necesita un tipo más complejo de lo que una enumeración de hacer lo que quieres, creo que es factible con una jerarquía de clases.
    • He publicado una solución que utiliza la jerarquía de clases, échale un vistazo.
  7. 1

    Si usted está preparado a la lista de su enumeración de los valores como parámetros de la plantilla que usted puede hacer esto en C++ 11 con varadic plantillas. Usted puede ver esto como una cosa buena, que permite aceptar los subconjuntos de la validez de los valores enum en diferentes contextos; a menudo útil al analizar los códigos de fuentes externas.

    Tal vez no es tan genérico como quieras, pero el código de comprobación de la misma es generalizado, sólo necesitas especificar el conjunto de valores. Este enfoque maneja lagunas, 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);
    }
    • Mientras que este vínculo puede responder a la pregunta, es mejor incluir a las partes esenciales de la respuesta aquí y proporcionar el enlace de referencia. Enlace-sólo respuestas puede ser válido si la página enlazada cambios. – De Revisión
    • Es un enlace a una diferente, ASÍ que la respuesta es, no tiene los mismos problemas que un enlace externo tiene. Actualizado en de todos modos.
  8. 0

    C++0x alternativa a la «fea» de la versión, permite varias enumeraciones. Utiliza el inicializador de listas en lugar de los conmutadores, un poco más limpio de la OMI. Por desgracia, esto no funciona en torno a la necesidad de codificar los valores de enumeración.

    #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 );
    }
    }

Dejar respuesta

Please enter your comment!
Please enter your name here