如何检查枚举值是否有效?

时间:2011-02-11 12:55:51

标签: c++ enums

我正在从二进制文件中读取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等)。

9 个答案:

答案 0 :(得分:22)

enum值在C ++中有效,如果它在[A,B]范围内,由下面的标准规则定义。因此,如果是enum X { A = 1, B = 3 }2的值将被视为有效的枚举值。

考虑标准的7.2 / 6:

对于枚举,其中emin是最小的枚举数且emax是最大的,枚举的值是bmin到bmax范围内的基础类型的值,其中bmin和bmax分别是最小值和最小值的最小值。可以存储emin和emax的位字段。可以定义具有未由其任何枚举​​器定义的值的枚举。

C ++中没有回顾。一种方法是另外列出数组中的枚举值,并编写一个可以进行转换的包装器,并可能在失败时抛出异常。

有关更多详细信息,请参阅Similar Question有关如何将int强制转换为枚举的信息。

答案 1 :(得分:9)

也许使用这样的枚举:

enum MyEnum
{
A,
B,
C
};

并检查

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

如果未指定枚举常量的值,则值从零开始,并在列表中每次向下移动时增加1。例如,给定 enum MyEnumType { ALPHA, BETA, GAMMA }; ALPHA的值为0,BETA的值为1,GAMMA的值为2.

答案 2 :(得分:9)

在C ++ 11中,如果您准备将枚举值列为模板参数,则有更好的方法。您可以将此视为一件好事,允许您在不同的上下文中接受有效枚举值的子集;在从外部源解析代码时通常很有用。

下面示例的一个可能有用的补充是围绕相对于IntType的基础类型EnumType的一些静态断言,以避免截断问题。留下来作为练习。

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

答案 3 :(得分:7)

我发现让它变得简单的唯一方法就是创建(宏)枚举的排序数组并用它进行检查。

switch诀窍因enum而失败,因为enum可能有多个具有给定值的枚举数。

这真是一个烦人的问题。

答案 4 :(得分:4)

C ++的托管扩展支持以下语法:

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

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

参考:MSDN&#34; Managed Extensions for C++ Programming&#34;

答案 5 :(得分:2)

谈到一种语言,没有更好的方法,枚举值只存在编译时间,并且无法以编程方式枚举它们。通过深思熟虑的基础架构,您仍然可以避免多次列出所有值。见Easy way to use variables of enum types as string in 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;
}

答案 6 :(得分:2)

Kinda necro,但是......对第一个/最后一个枚举值进行了RANGE检查(可以与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声明:

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

用法:

bool r = testEnumRange<MinMaxType>(i);

主要区别在于提出测试函数仅依赖于枚举类型本身。

答案 7 :(得分:1)

一种无需查找的可能解决方案 - 枚举值应该是素数

此解决方案并不通用:

  • 不适用于标志枚举
  • 不适用于大型枚举或大型枚举值(溢出)
  • 可能难以保持一致性

但实用:

  • O(1) 复杂度

  • 能够添加 INVALID=1 作为错误指示的枚举定义

代码:

#include<iostream>
#include <cstdint>
#include <vector>
enum class Some :uint64_t{
   A=2,
   B=3,
   C=5,
   D=7,
   E=11,
   F=13,
// etc. just stick to primes
};
static constexpr uint64_t some_checksum = static_cast<uint64_t>(Some::A)*
static_cast<uint64_t>(Some::B)*
static_cast<uint64_t>(Some::C)*
static_cast<uint64_t>(Some::D)*
static_cast<uint64_t>(Some::E)*
static_cast<uint64_t>(Some::F);

constexpr bool is_some(uint64_t v) {
    return some_checksum % v == 0;
}
constexpr bool get_some(uint64_t v, Some& out){
    if (some_checksum % v == 0) {
        out = static_cast<Some>(v);
        return true;
    }
    return false;//Something to indicate an error;
}

int main(int v) {
    Some s;
    if (get_some(v, s)){
        std::cout << "Ok\n" << static_cast<int>(s) << "\n"; 
    } else {
        std::cout << "No\n";
    }

}

答案 8 :(得分:0)

另一种方法:

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

&#34;丑陋&#34;这种方法的一部分恕我直言,必须定义:

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;