Как проверить, допустимо ли значение enum? - PullRequest
43 голосов
/ 11 февраля 2011

Я читаю значение 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 или есть лучший способ?

РЕДАКТИРОВАТЬ

У меня установлены значения enum, и, к сожалению, я не могу их изменить.Что еще хуже, они не являются непрерывными (их значения равны 0, 75,76,80,85,90,95,100 и т. Д.)

Ответы [ 8 ]

23 голосов
/ 11 февраля 2011

enum значение действительно в C ++, если оно попадает в диапазон [A, B], который определяется стандартным правилом ниже.Таким образом, в случае enum X { A = 1, B = 3 } значение 2 считается допустимым значением перечисления.

Рассмотрим 7.2 / 6 стандарта:

Для перечисления, где emin является наименьшим перечислителем иemax является наибольшим, значения перечисления являются значениями базового типа в диапазоне от bmin до bmax, где bmin и bmax являются, соответственно, наименьшим и наибольшим значениями наименьшего битового поля, которое может хранить emin и emax.Можно определить перечисление, значения которого не определены ни одним из перечислителей.

В C ++ нет ретроспекции.Один из подходов состоит в том, чтобы дополнительно перечислить перечислимые значения в массиве и написать оболочку, которая будет выполнять преобразование и, возможно, генерировать исключение при ошибке.

См. Аналогичный вопрос о том, как привести int кenum для дальнейших деталей.

9 голосов
/ 13 октября 2015

В C ++ 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);
}
9 голосов
/ 11 февраля 2011

Может быть использовать перечисление следующим образом:

enum MyEnum
{
A,
B,
C
};

и для проверки

if (v2 >= A && v2 <= C)

Если вы не укажете значения для констант перечисления, значения начинаются с нуля и увеличиваются на единицус каждым движением вниз по списку.Например, если enum MyEnumType { ALPHA, BETA, GAMMA }; ALPHA имеет значение 0, BETA имеет значение 1, а GAMMA имеет значение 2.

7 голосов
/ 11 февраля 2011

Единственный способ сделать это «простым» - это создать (макрос) отсортированный массив перечислений и проверить его.

Трюк switch не удался с enum sпотому что enum может иметь более одного перечислителя с заданным значением.

Это действительно раздражающая проблема.

4 голосов
/ 16 августа 2014

Управляемые расширения для C ++ поддерживают следующий синтаксис:

enum Abc
{
    A = 4,
    B = 8,
    C = 12
};

Enum::IsDefined(Abc::typeid, 8);

Ссылка: MSDN " Управляемые расширения для программирования на C ++ "

3 голосов
/ 04 декабря 2015

Kinda necro, но ... выполняет RANGE проверку int на значения первого / последнего перечисления (может сочетаться с идеей janm для точных проверок), C ++ 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 MinMaxType
{
     Max = 0x800, Min, Equal
};
DECLARE_ENUM_CHECK(MinMaxType, MinMaxType::Max, MinMaxType::Equal);

Использование:

bool r = testEnumRange<MinMaxType>(i);

Преимущественно отличается от предложенного выше, что тестовая функция зависит только от самого типа перечисления.

2 голосов
/ 11 февраля 2011

Говоря о языке, лучшего способа нет, перечислимые значения существуют только во время компиляции, и нет способа перечислить их программно.Тем не менее, с продуманной инфраструктурой вы все равно сможете избежать перечисления всех значений несколько раз.См. Простой способ использовать переменные типов 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;
}
0 голосов
/ 18 сентября 2017

Еще один способ сделать это:

#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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...