如何检查枚举值是否有效?
我正在读取一个二进制文件的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等)
enum
值落在由下面的标准规则定义的范围[A,B]中,则在C ++中有效。 所以在enum X { A = 1, B = 3 }
,值2
被认为是一个有效的枚举值。
考虑标准的7.2 / 6:
对于其中emin是最小枚举数并且emax是最大的枚举,枚举的值是在bmin到bmax范围内的基础types的值,其中bmin和bmax分别是最小值和最大值可以存储emin和emax的位字段。 有可能定义一个枚举值,它的值不是由任何枚举器定义的。
在C ++中没有回顾。 一种方法是额外列出数组中的枚举值,并编写一个可以进行转换的包装器,并可能在失败时抛出exception。
看到类似的问题关于如何投入整数枚举进一步的细节。
也许使用这样的枚举:
enum MyEnum { A, B, C };
并检查
if (v2 >= A && v2 <= C)
如果您不指定枚举常量的值,则值从零开始,每次向下移动列表中的值。 例如,给定enum MyEnumType { ALPHA, BETA, GAMMA };
ALPHA的值为0,BETA的值为1,GAMMA的值为2。
我发现“简单”的唯一方法是创build(macros)一个sorting的枚举数组,然后用它来检查。
switch
技巧因enum
而失败,因为enum
可能有多个具有给定值的枚举器。
这真是一个烦人的问题。
在C ++ 11中,如果您准备将枚举值列为模板参数,则有更好的方法。 您可以将此视为一件好事,让您在不同的上下文中接受有效枚举值的子集; 在parsing来自外部的代码时通常很有用。
下面的例子中可能有用的补充是围绕EnumType相对于IntType的基本types的一些静态断言,以避免截断问题。 留作练习。
#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); }
C ++托pipe扩展支持以下语法:
enum Abc { A = 4, B = 8, C = 12 }; Enum::IsDefined(Abc::typeid, 8);
参考:MSDN“ 用于C ++编程的托pipe扩展 ”
说到一种语言,没有更好的办法,枚举值只存在编译时间,没有办法枚举它们的编程方式。 但是,通过一个精心devise的基础架构,您仍然可以避免多次列出所有值。 请参阅简单的方法来使用枚举types的variables作为C中的string?
然后可以使用提供的“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; }
有点necro,但是…做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);
以上主要区别仅在于testing函数依赖于枚举types本身。
还有另一种方法来做到这一点:
#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
如果你不反对macros,你可以把它包装在一个macros中:
#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;