c++ - imprimir - ¿Cómo verificar si el valor enum es válido?
imprimir enum en c (8)
En C ++ 11 hay una manera mejor si está preparado para enumerar sus valores enum como parámetros de plantilla. 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.
Una posible adición útil al siguiente ejemplo sería algunas aserciones estáticas alrededor del tipo subyacente de EnumType relativo a IntType para evitar problemas de truncamiento. Izquierda como ejercicio.
#include <stdio.h>
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<IntType>(V) || super::is_value(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);
}
Estoy leyendo un valor enum
de un archivo binario y me gustaría comprobar si el valor es realmente parte de los valores enum
. ¿Cómo puedo hacerlo?
#include <iostream>
enum Abc
{
A = 4,
B = 8,
C = 12
};
int main()
{
int v1 = 4;
Abc v2 = static_cast< Abc >( v1 );
switch ( v2 )
{
case A:
std::cout<<"A"<<std::endl;
break;
case B:
std::cout<<"B"<<std::endl;
break;
case C:
std::cout<<"C"<<std::endl;
break;
default :
std::cout<<"no match found"<<std::endl;
}
}
¿Tengo que usar el operador del switch
o hay una mejor manera?
EDITAR
Tengo valores enum establecidos y desafortunadamente no puedo modificarlos. Para empeorar las cosas, no son continuos (sus valores van de 0, 75,76,80,85,90,95,100, etc.)
Hablando de un idioma, no hay mejor manera, los valores enum existen solo en tiempo de compilación y no hay forma de enumerarlos programáticamente. Sin embargo, con una infraestructura bien pensada, aún podrá evitar enumerar todos los valores varias veces. Consulte la forma fácil de usar variables de tipos enum como cadena en C?
Su muestra puede ser reescrita utilizando el "enumFactory.h" proporcionado allí como:
#include "enumFactory.h"
#define ABC_ENUM(XX) /
XX(A,=4) /
XX(B,=8) /
XX(C,=12) /
DECLARE_ENUM(Abc,ABC_ENUM)
int main()
{
int v1 = 4;
Abc v2 = static_cast< Abc >( v1 );
#define CHECK_ENUM_CASE(name,assign) case name: std::cout<< #name <<std::endl; break;
switch ( v2 )
{
ABC_ENUM(CHECK_ENUM_CASE)
default :
std::cout<<"no match found"<<std::endl;
}
#undef CHECK_ENUM_CASE
}
o incluso (usando algunas instalaciones más que ya existen en ese encabezado):
#include "enumFactory.h"
#define ABC_ENUM(XX) /
XX(A,=4) /
XX(B,=8) /
XX(C,=12) /
DECLARE_ENUM(Abc,ABC_ENUM)
DEFINE_ENUM(Abc,ABC_ENUM)
int main()
{
int v1 = 4;
Abc v2 = static_cast< Abc >( v1 );
const char *name = GetString(v2);
if (name[0]==0) name = "no match found";
std::cout << name << std::endl;
}
La única forma que he encontrado para hacerlo "fácil", fue crear (macro) una matriz ordenada de las enumeraciones y verificar con eso.
El truco del switch
falla con las enum
porque una enum
puede tener más de un enumerador con un valor dado.
Es un problema molesto, de verdad.
Managed Extensions para C ++ admite la siguiente sintaxis:
enum Abc
{
A = 4,
B = 8,
C = 12
};
Enum::IsDefined(Abc::typeid, 8);
Referencia: MSDN " Extensiones administradas para la programación en C ++ "
Otra forma de hacerlo:
#include <algorithm>
#include <iterator>
#include <iostream>
template<typename>
struct enum_traits { static constexpr void* values = nullptr; };
namespace detail
{
template<typename T>
constexpr bool is_value_of(int, void*) { return false; }
template<typename T, typename U>
constexpr bool is_value_of(int v, U)
{
using std::begin; using std::end;
return std::find_if(begin(enum_traits<T>::values), end(enum_traits<T>::values),
[=](auto value){ return value == static_cast<T>(v); }
) != end(enum_traits<T>::values);
}
}
template<typename T>
constexpr bool is_value_of(int v)
{ return detail::is_value_of<T>(v, decltype(enum_traits<T>::values) { }); }
////////////////////
enum Abc { A = 4, B = 8, C = 12 };
template<>
struct enum_traits<Abc> { static constexpr auto values = { A, B, C }; };
decltype(enum_traits<Abc>::values) enum_traits<Abc>::values;
enum class Def { D = 1, E = 3, F = 5 };
int main()
{
std::cout << "Abc:";
for(int i = 0; i < 10; ++i)
if(is_value_of<Abc>(i)) std::cout << " " << i;
std::cout << std::endl;
std::cout << "Def:";
for(int i = 0; i < 10; ++i)
if(is_value_of<Def>(i)) std::cout << " " << i;
std::cout << std::endl;
return 0;
}
La parte "fea" de este enfoque en mi humilde opinión debe definir:
decltype(enum_traits<Abc>::values) enum_traits<Abc>::values
Si no se opone a las macros, puede envolverlo dentro de una macro:
#define REGISTER_ENUM_VALUES(name, ...) /
template<> struct enum_traits<name> { static constexpr auto values = { __VA_ARGS__ }; }; /
decltype(enum_traits<name>::values) enum_traits<name>::values;
Tal vez use enum así:
enum MyEnum
{
A,
B,
C
};
y para verificar
if (v2 >= A && v2 <= C)
Si no especifica valores para las constantes enum, los valores comienzan en cero y aumentan en uno con cada movimiento hacia abajo en la lista. Por ejemplo, dado enum MyEnumType { ALPHA, BETA, GAMMA };
ALPHA tiene un valor de 0, BETA tiene un valor de 1 y GAMMA tiene un valor de 2.
Un poco necro, pero ... hace una comprobación de rango de int en los valores de enum primero / último (se puede combinar con la idea de janm para hacer comprobaciones exactas), C ++ 11:
Encabezamiento:
namespace chkenum
{
template <class T, T begin, T end>
struct RangeCheck
{
private:
typedef typename std::underlying_type<T>::type val_t;
public:
static
typename std::enable_if<std::is_enum<T>::value, bool>::type
inrange(val_t value)
{
return value >= static_cast<val_t>(begin) && value <= static_cast<val_t>(end);
}
};
template<class T>
struct EnumCheck;
}
#define DECLARE_ENUM_CHECK(T,B,E) namespace chkenum {template<> struct EnumCheck<T> : public RangeCheck<T, B, E> {};}
template<class T>
inline
typename std::enable_if<std::is_enum<T>::value, bool>::type
testEnumRange(int val)
{
return chkenum::EnumCheck<T>::inrange(val);
}
Declaración de Enum:
enum MinMaxType
{
Max = 0x800, Min, Equal
};
DECLARE_ENUM_CHECK(MinMaxType, MinMaxType::Max, MinMaxType::Equal);
Uso:
bool r = testEnumRange<MinMaxType>(i);
Principalmente la diferencia de arriba lo propuso que la función de prueba depende solamente del tipo de enumeración.
enum
valor enum
es válido en C ++ si está dentro del rango [A, B], que se define mediante la regla estándar a continuación. Entonces, en caso de enum X { A = 1, B = 3 }
, el valor de 2
se considera un valor enum válido.
Considere 7.2 / 6 de estándar:
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.
No hay retrospección en C ++. Un enfoque a seguir es enumerar los valores enum en una matriz adicionalmente y escribir una envoltura que haría la conversión y posiblemente lanzar una excepción en caso de falla.
Vea una pregunta similar sobre cómo convertir int a enum para más detalles.