在int中枚举枚举的通用方法
有没有一种通用的方式来int
enum
在C++
enum
?
如果int
落在一个enum
范围内,它应该返回一个enum
值,否则抛出一个exception
。 有没有一种方法来一般地写? 应该支持多于一个enum type
。
背景:我有一个外部枚举types, 不能控制源代码。 我想将这个值存储在数据库中并检索它。
显而易见的是注释你的枚举:
// generic code #include <algorithm> template <typename T> struct enum_traits {}; template<typename T, size_t N> T *endof(T (&ra)[N]) { return ra + N; } template<typename T, typename ValType> T check(ValType v) { typedef enum_traits<T> traits; const T *first = traits::enumerators; const T *last = endof(traits::enumerators); if (traits::sorted) { // probably premature optimization if (std::binary_search(first, last, v)) return T(v); } else if (std::find(first, last, v) != last) { return T(v); } throw "exception"; } // "enhanced" definition of enum enum e { x = 1, y = 4, z = 10, }; template<> struct enum_traits<e> { static const e enumerators[]; static const bool sorted = true; }; // must appear in only one TU, // so if the above is in a header then it will need the array size const e enum_traits<e>::enumerators[] = {x, y, z}; // usage int main() { e good = check<e>(1); e bad = check<e>(2); }
你需要和e
保持一致,如果你不是e
的作者,这是一个讨厌的东西。 正如Sjoerd所说,任何体面的构build系统都可以实现自动化。
无论如何,你是对7.2 / 6:
对于其中emin是最小枚举数并且emax是最大的枚举,枚举的值是在bmin到bmax范围内的基础types的值,其中bmin和bmax分别是最小值和最大值可以存储emin和emax的位字段。 有可能定义一个枚举值,它的值不是由任何枚举器定义的。
因此,如果您不是e
的作者,您可能会也可能不会保证e
有效值实际上出现在其定义中。
丑陋。
enum MyEnum { one = 1, two = 2 }; MyEnum to_enum(int n) { switch( n ) { case 1 : return one; case 2 : return two; } throw something(); }
现在是真正的问题。 你为什么需要这个? 代码是丑陋的,不容易写(*?),不容易维护,不容易纳入你的代码。 它告诉你这是错误的代码。 为什么要打呢?
编辑:
另外,考虑到枚举是C ++中的整型:
enum my_enum_val = static_cast<MyEnum>(my_int_val);
但是上面更丑陋,更容易出错,而且不会像你想的那样扔掉。
如果像你所描述的那样,这些值是在一个数据库中的,那么为什么不写一个代码生成器来读取这个表,并用enum和to_enum(int)
函数创build一个.h和.cpp文件?
优点:
- 轻松添加
to_string(my_enum)
函数。 - 需要less量维护
- 数据库和代码是同步的
不,C ++没有反省,也没有任何内build的“域名检查”function。
你觉得这个怎么样?
#include <iostream> #include <stdexcept> #include <set> #include <string> using namespace std; template<typename T> class Enum { public: static void insert(int value) { _set.insert(value); } static T buildFrom(int value) { if (_set.find(value) != _set.end()) { T retval; retval.assign(value); return retval; } throw std::runtime_error("unexpected value"); } operator int() const { return _value; } private: void assign(int value) { _value = value; } int _value; static std::set<int> _set; }; template<typename T> std::set<int> Enum<T>::_set; class Apples: public Enum<Apples> {}; class Oranges: public Enum<Oranges> {}; class Proxy { public: Proxy(int value): _value(value) {} template<typename T> operator T() { T theEnum; return theEnum.buildFrom(_value); } int _value; }; Proxy convert(int value) { return Proxy(value); } int main() { Apples::insert(4); Apples::insert(8); Apples a = convert(4); // works std::cout << a << std::endl; // prints 4 try { Apples b = convert(9); // throws } catch (std::exception const& e) { std::cout << e.what() << std::endl; // prints "unexpected value" } try { Oranges b = convert(4); // also throws } catch (std::exception const& e) { std::cout << e.what() << std::endl; // prints "unexpected value" } }
然后你可以使用我在这里发布的代码来打开值。
你不应该想像你描述的那样存在,我担心你的代码devise有问题。
另外,你认为枚举来自一个范围,但并非总是如此:
enum Flags { one = 1, two = 2, four = 4, eigh = 8, big = 2000000000 };
这不在一个范围内:即使有可能,你是否应该检查从0到2 ^ n的每个整数,看看它们是否匹配一些枚举的值?
C ++ 0x替代“丑陋”的版本,允许多个枚举。 使用初始化列表,而不是开关,有点清洁IMO。 不幸的是,这不需要硬编码枚举值。
#include <cassert> // assert namespace // unnamed namespace { enum class e1 { value_1 = 1, value_2 = 2 }; enum class e2 { value_3 = 3, value_4 = 4 }; template <typename T> int valid_enum( const int val, const T& vec ) { for ( const auto item : vec ) if ( static_cast<int>( item ) == val ) return val; throw std::exception( "invalid enum value!" ); // throw something useful here } // valid_enum } // ns int main() { // generate list of valid values const auto e1_valid_values = { e1::value_1, e1::value_2 }; const auto e2_valid_values = { e2::value_3, e2::value_4 }; auto result1 = static_cast<e1>( valid_enum( 1, e1_valid_values ) ); assert( result1 == e1::value_1 ); auto result2 = static_cast<e2>( valid_enum( 3, e2_valid_values ) ); assert( result2 == e2::value_3 ); // test throw on invalid value try { auto result3 = static_cast<e1>( valid_enum( 9999999, e1_valid_values ) ); assert( false ); } catch ( ... ) { assert( true ); } }
如果你准备列举你的枚举值作为模板参数,你可以在C ++ 11中使用可变模板来完成。 您可以将此视为一件好事,让您在不同的上下文中接受有效枚举值的子集; 在parsing来自外部的代码时通常很有用。
也许不像你想的那么通用,但是检查代码本身是一般化的,你只需要指定一组值。 这种方法处理缺口,任意值等
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<typename std::underlying_type<EnumType>::type>(V) || super::is_value(v); } EnumType convert(IntType v) { if (!is_value(v)) throw std::runtime_error("Enum value out of range"); return static_cast<EnumType>(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); }
尝试这样的事情:
enum EType { type1, type2 }; unsigned int number = 3; EType e = static_cast<EType>(number); if(static_cast<unsigned int>(e) != number) throw std::exception();