How to check if enum value is valid?

前端 未结 8 1243
一向
一向 2020-11-27 06:38

I am reading an enum value from a binary file and would like to check if the value is really part of the enum values. How can I do it?



        
相关标签:
8条回答
  • 2020-11-27 07:11

    Maybe use enum like this:

    enum MyEnum
    {
    A,
    B,
    C
    };
    

    and to check

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

    If you don't specify values for enum constants, the values start at zero and increase by one with each move down the list. For example, given enum MyEnumType { ALPHA, BETA, GAMMA }; ALPHA has a value of 0, BETA has a value of 1, and GAMMA has a value of 2.

    0 讨论(0)
  • 2020-11-27 07:16

    In C++ 11 there is a better way if you are prepared to list your enum values as template parameters. You can look at this as a good thing, allowing you to accept subsets of the valid enum values in different contexts; often useful when parsing codes from external sources.

    A possible useful addition to the example below would be some static assertions around the underlying type of EnumType relative to IntType to avoid truncation issues. Left as an exercise.

    #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);
    }
    
    0 讨论(0)
  • 2020-11-27 07:16

    Yet another way to do it:

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

    The "ugly" part of this approach IMHO is having to define:

    decltype(enum_traits<Abc>::values) enum_traits<Abc>::values
    

    If you are not opposed to macros, you can wrap it inside a 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;
    
    0 讨论(0)
  • 2020-11-27 07:20

    Speaking about a language, there is no better way, the enum values exist compile time only and there is no way to enumerate them programatically. With a well thought infrastructure you may still be able to avoid listing all values several times, though. See Easy way to use variables of enum types as string in C?

    Your sample can then be rewritten using the "enumFactory.h" provided there as:

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

    or even (using some more facilities already existing in that header):

    #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 讨论(0)
  • 2020-11-27 07:22

    Kinda necro, but ... makes a RANGE check of int into first/last enum values (can be combined with janm's idea to make exact checks), C++11:

    Header:

    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 declaration:

    enum MinMaxType
    {
         Max = 0x800, Min, Equal
    };
    DECLARE_ENUM_CHECK(MinMaxType, MinMaxType::Max, MinMaxType::Equal);
    

    Usage:

    bool r = testEnumRange<MinMaxType>(i);
    

    Mainly difference of above proposed it that test function is dependent on enum type itself only.

    0 讨论(0)
  • 2020-11-27 07:23

    The only way I ever found to make it 'easy', was to create (macro) a sorted array of the enums and checking with that.

    The switch trick fail with enums because an enum may have more than one enumerator with a given value.

    It's an annoying issue, really.

    0 讨论(0)
提交回复
热议问题