Как проверить, действительно ли значение enum?
Я читаю значение enum
из двоичного файла и хочу проверить, действительно ли значение является частью значений enum
. Как я могу это сделать?
#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;
}
}
Нужно ли использовать оператор switch
или есть лучший способ?
ИЗМЕНИТЬ
У меня есть значения перечисления, и, к сожалению, я не могу их изменить. Чтобы ухудшить ситуацию, они не являются непрерывными (их значения равны 0, 75,76,80,85,90,95,100 и т.д.).
Ответы
Ответ 1
enum
Значение действительно в С++, если оно попадает в диапазон [A, B], который определяется стандартным правилом ниже. Поэтому в случае enum X { A = 1, B = 3 }
значение 2
считается допустимым значением перечисления.
Рассмотрим 7.2/6 стандарта:
Для перечисления, где emin - наименьший счетчик, а emax - самый большой, значения перечисления являются значениями базового типа в диапазоне bmin до bmax, где bmin и bmax являются соответственно наименьшими и наибольшими значениями наименьшего бит-поле, которое может хранить emin и emax. Можно определить перечисление, которое имеет значения, не определенные каким-либо из его счетчиков.
В С++ нет ретроспекции. Один из подходов - перечислить значения enum в массиве дополнительно и написать оболочку, которая будет делать преобразование и, возможно, выдать исключение при ошибке.
См. Подобный вопрос о том, как отличить int для перечисления для более подробной информации.
Ответ 2
Возможно, используйте перечисление следующим образом:
enum MyEnum
{
A,
B,
C
};
и проверить
if (v2 >= A && v2 <= C)
Если вы не укажете значения для констант перечисления, значения начинаются с нуля и увеличиваются на единицу при каждом перемещении вниз по списку. Например, данный
enum MyEnumType { ALPHA, BETA, GAMMA };
ALPHA имеет значение 0, BETA имеет значение 1, а GAMMA имеет значение 2.
Ответ 3
Единственный способ, который я когда-либо нашел, чтобы сделать его "легким", состоял в том, чтобы создать (макрос) отсортированный массив перечислений и проверить с ним.
Сбой switch
с enum
, потому что enum
может иметь более одного счетчика с заданным значением.
Это очень неприятная проблема.
Ответ 4
В С++ 11 есть лучший способ, если вы готовы перечислить свои значения перечисления в качестве параметров шаблона. Вы можете рассматривать это как хорошую вещь, позволяющую принимать подмножества действительных значений перечисления в разных контекстах; часто полезно при анализе кодов из внешних источников.
Возможным полезным дополнением к приведенному ниже примеру были бы некоторые статические утверждения вокруг базового типа EnumType относительно IntType, чтобы избежать проблем с усечением. Оставленный как упражнение.
#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);
}
Ответ 5
Управляемые расширения для С++ поддерживают следующий синтаксис:
enum Abc
{
A = 4,
B = 8,
C = 12
};
Enum::IsDefined(Abc::typeid, 8);
Ссылка: MSDN " Управляемые расширения для программирования на С++
Ответ 6
Говоря о языке, нет лучшего способа, значения enum существуют только для времени компиляции, и нет возможности перечислить их программно. Вместе с хорошо продуманной инфраструктурой вы все равно можете избежать перечисления всех значений несколько раз. См. Простой способ использования переменных типов перечисления как строки в C?
Затем ваш образец можно переписать с помощью "enumFactory.h" при условии, что:
#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
}
или даже (используя еще несколько объектов, уже существующих в этом заголовке):
#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;
}
Ответ 7
Kinda necro, но... делает проверку RANGE в int в значениях первого и последнего enum (может быть объединена с идеей janm для точной проверки), С++ 11:
Заголовок:
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);
}
Объявление enum:
enum MinMaxType
{
Max = 0x800, Min, Equal
};
DECLARE_ENUM_CHECK(MinMaxType, MinMaxType::Max, MinMaxType::Equal);
Использование:
bool r = testEnumRange<MinMaxType>(i);
В основном отличие выше предлагалось, что тестовая функция зависит только от типа перечисления.
Ответ 8
Еще один способ сделать это:
#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;
}
"Уродливая" часть этого подхода ИМХО должна определить:
decltype(enum_traits<Abc>::values) enum_traits<Abc>::values
Если вы не против макросов, вы можете обернуть его внутри макроса:
#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;