迭代非增量枚举

在你问之前,我已经看了 看 ,并find了这个,并找不到一个坚实的答案。

我需要能够dynamic迭代具有非增量值的枚举,例如:

typedef enum { CAPI_SUBTYPE_NULL = 0, /* Null subtype. */ CAPI_SUBTYPE_DIAG_DFD = 1, /* Data Flow diag. */ CAPI_SUBTYPE_DIAG_ERD = 2, /* Entity-Relationship diag. */ CAPI_SUBTYPE_DIAG_STD = 3, /* State Transition diag. */ CAPI_SUBTYPE_DIAG_STC = 4, /* Structure Chart diag. */ CAPI_SUBTYPE_DIAG_DSD = 5, /* Data Structure diag. */ CAPI_SUBTYPE_SPEC_PROCESS = 6, /* Process spec. */ CAPI_SUBTYPE_SPEC_MODULE = 7, /* Module spec. */ CAPI_SUBTYPE_SPEC_TERMINATOR = 8, /* Terminator spec. */ CAPI_SUBTYPE_DD_ALL = 13, /* DD Entries (All). */ CAPI_SUBTYPE_DD_COUPLE = 14, /* DD Entries (Couples). */ CAPI_SUBTYPE_DD_DATA_AREA = 15, /* DD Entries (Data Areas). */ CAPI_SUBTYPE_DD_DATA_OBJECT = 16, /* DD Entries (Data Objects). */ CAPI_SUBTYPE_DD_FLOW = 17, /* DD Entries (Flows). */ CAPI_SUBTYPE_DD_RELATIONSHIP = 18, /* DD Entries (Relationships). */ CAPI_SUBTYPE_DD_STORE = 19, /* DD Entries (Stores). */ CAPI_SUBTYPE_DIAG_PAD = 35, /* Physical architecture diagram. */ CAPI_SUBTYPE_DIAG_BD = 36, /* Behaviour diagram. */ CAPI_SUBTYPE_DIAG_UCD = 37, /* UML Use case diagram. */ CAPI_SUBTYPE_DIAG_PD = 38, /* UML Package diagram. */ CAPI_SUBTYPE_DIAG_COD = 39, /* UML Collaboration diagram. */ CAPI_SUBTYPE_DIAG_SQD = 40, /* UML Sequence diagram. */ CAPI_SUBTYPE_DIAG_CD = 41, /* UML Class diagram. */ CAPI_SUBTYPE_DIAG_SCD = 42, /* UML State chart. */ CAPI_SUBTYPE_DIAG_ACD = 43, /* UML Activity chart. */ CAPI_SUBTYPE_DIAG_CPD = 44, /* UML Component diagram. */ CAPI_SUBTYPE_DIAG_DPD = 45, /* UML Deployment diagram. */ CAPI_SUBTYPE_DIAG_PFD = 47, /* Process flow diagram. */ CAPI_SUBTYPE_DIAG_HIER = 48, /* Hierarchy diagram. */ CAPI_SUBTYPE_DIAG_IDEF0 = 49, /* IDEF0 diagram. */ CAPI_SUBTYPE_DIAG_AID = 50, /* AID diagram. */ CAPI_SUBTYPE_DIAG_SAD = 51, /* SAD diagram. */ CAPI_SUBTYPE_DIAG_ASG = 59 /* ASG diagram. */ } CAPI_SUBTYPE_E ; 

我希望能够做到这一点的原因是因为枚举是在一个API(我不能改变,显然),并希望能够,无论API版本,都能够迭代这些值。

任何方向表示赞赏。

使用C ++,遍历枚举的唯一方法是将它们存储在一个数组中,并通过相同的迭代进行迭代。 主要的挑战是如何跟踪enum声明和数组声明中的相同顺序?
你可以自动化你在enum和数组中sorting的方式。 我觉得这是一个体面的方式:

 // CAPI_SUBTYPE_E_list.h // This header file contains all the enum in the order // Whatever order is set will be followed everywhere NAME_VALUE(CAPI_SUBTYPE_NULL, 0), /* Null subtype. */ NAME_VALUE(CAPI_SUBTYPE_DIAG_DFD, 1), /* Data Flow diag. */ NAME_VALUE(CAPI_SUBTYPE_DIAG_ERD, 2), /* Entity-Relationship diag. */ ... NAME_VALUE(CAPI_SUBTYPE_DD_ALL, 13), /* DD Entries (All). */ NAME_VALUE(CAPI_SUBTYPE_DD_COUPLE, 14), /* DD Entries (Couples). */ ... NAME_VALUE(CAPI_SUBTYPE_DIAG_ASG, 59) /* ASG diagram. */ 

现在你在包含这个文件的枚举声明和数组声明中都有macros定义:

 // Enum.h typedef enum { #define NAME_VALUE(NAME,VALUE) NAME = VALUE #include"CAPI_SUBTYPE_E_list.h" #undef NAME_VALUE }CAPI_SUBTYPE_E; 

并把其他macros的定义放在同一个文件中:

 // array file // Either this array can be declared `static` or inside unnamed `namespace` to make // ... it visible through a header file; Or it should be declared `extern` and keep ... // ... the record of its size; declare a getter method for both array and the size unsigned int CAPI_SUBTYPE_E_Array [] = { #define NAME_VALUE(NAME,VALUE) NAME #include"CAPI_SUBTYPE_E_list.h" #undef NAME_VALUE }; 

现在在C ++ 03中迭代:

 for(unsigned int i = 0, size = sizeof(CAPI_SUBTYPE_E_Array)/sizeof(CAPI_SUBTYPE_E_Array[0]); i < size; ++i) 

或者在C ++ 11中简单:

 for(auto i : CAPI_SUBTYPE_E_Array) 

它比C ++实践更复杂,更C,但可以使用Xmacros。 这是非常丑陋的,你需要保持正确的顺序表。 在C ++中,我相信我们不需要迭代枚举,而且我们也不需要为枚举赋值(表面上枚举值在每个编译中都是随机的)。 所以认为这是一个笑话:)

 #include <iostream> #define CAPI_SUBTYPE_TABLE \ CAPI_SUBTYPE_X(CAPI_SUBTYPE_NULL, 0 ) \ CAPI_SUBTYPE_X(CAPI_SUBTYPE_DIAG_DFD, 1 ) \ CAPI_SUBTYPE_X(CAPI_SUBTYPE_DD_ALL, 13) #define CAPI_SUBTYPE_X(name, value) name = value, enum CAPI_SUBTYPE { CAPI_SUBTYPE_TABLE CAPI_SUBTYPE_END }; #undef CAPI_SUBTYPE_X #define CAPI_SUBTYPE_X(name, value) name, CAPI_SUBTYPE subtype_iteratable[] = { CAPI_SUBTYPE_TABLE CAPI_SUBTYPE_END }; #undef CAPI_SUBTYPE_X #define CAPI_SUBTYPE_SIZE (sizeof(subtype_iteratable) / sizeof(subtype_iteratable[0]) - 1) int main() { for (unsigned i = 0; i < CAPI_SUBTYPE_SIZE; ++i) std::cout << subtype_iteratable[i] << std::endl; // 0, 1, 13 } 

我同意已经发表的声明,如果不修改或复制enum的定义,这是不可能的。 但是,在C ++ 11中(甚至可能是C ++ 03),只要提供一个语法,你所要做的就是从enum复制和粘贴枚举器定义到macros中。 只要每个枚举器都有一个明确的定义(使用= ),就可以工作。

编辑:你可以展开这个工作,即使不是每个枚举都有一个明确的定义,但这不应该在这种情况下需要。

我曾经为一些物理学家开发过,所以这个例子是关于粒子的。

用法示例:

 // required for this example #include <iostream> enum ParticleEnum { PROTON = 11, ELECTRON = 42, MUON = 43 }; // define macro (see below) MAKE_ENUM( ParticleEnum, // name of enum type particle_enum_detail, // some namespace to place some types in all_particles, // name of array to list all enumerators // paste the enumerator definitions of your enum here PROTON = 11, ELECTRON = 42, MUON = 43 ) // don't forget the macro's closing paranthesis int main() { for(ParticleEnum p : all_particles) { std::cout << p << ", "; } } 

这个macros产生(有效):

 namespace particle_enum_detail { // definition of a type and some constants constexpr ParticleEnum all_particles[] = { PROTON, ELECTRON, MUON }; } using particle_enum_detail::all_particles; 

macros定义

 #define MAKE_ENUM(ENUM_TYPE, NAMESPACE, ARRAY_NAME, ...) \ namespace NAMESPACE \ { \ struct iterable_enum_ \ { \ using storage_type = ENUM_TYPE; \ template < typename T > \ constexpr iterable_enum_(T p) \ : m{ static_cast<storage_type>(p) } \ {} \ constexpr operator storage_type() \ { return m; } \ template < typename T > \ constexpr iterable_enum_ operator= (T p) \ { return { static_cast<storage_type>(p) }; } \ private: \ storage_type m; \ }; \ \ /* the "enumeration" */ \ constexpr iterable_enum_ __VA_ARGS__; \ /* the array to store all "enumerators" */ \ constexpr ENUM_TYPE ARRAY_NAME[] = { __VA_ARGS__ }; \ } \ using NAMESPACE::ARRAY_NAME; // macro end 

注意:typesiterable_enum_也可以在macros之外定义一次。


macros观解释

这个想法是在macros调用中允许像proton = 11, electron = 12这样的语法。 这对于任何types的声明都是非常容易的,但是对于存储这些名字会造成问题:

 #define MAKE_ENUM(ASSIGNMEN1, ASSIGNMENT2) \ enum my_enum { ASSIGNMENT1, ASSIGNMENT2 }; \ my_enum all[] = { ASSIGNMENT1, ASSIGNMENT2 }; MAKE_ENUM(proton = 11, electron = 22); 

屈服于:

 enum my_enum { proton = 11, electron = 22 }; // would be OK my_enum all[] = { proton = 11, electron = 22 }; // cannot assign to enumerator 

与许多语法技巧一样,运算符重载提供了一种方法来解决这个问题。 但赋值运算符必须是成员函数 – 枚举不是类。 那么为什么不使用一些常量对象而不是枚举呢?

 enum my_enum { proton = 11, electron = 22 }; // alternatively constexpr int proton = 11, electron = 12; // the `constexpr` here is equivalent to a `const` 

这还没有解决我们的问题,它只是说明如果我们不需要枚举器的自动递增function,我们可以很容易地用常量列表replace枚举

现在,操作符重载的语法窍门是:

 struct iterable_enum_ { // the trick: a constexpr assignment operator constexpr iterable_enum_ operator= (int p) // (op) { return {p}; } // we need a ctor for the syntax `object = init` constexpr iterable_enum_(int p) // (ctor) : m{ static_cast<ParticleEnum>(p) } {} private: ParticleEnum m; }; constexpr iterable_enum_ proton = 11, electron = 22; // (1) iterable_enum_ all_particles[] = { proton = 11, electron = 22 }; // (2) 

诀窍是,在行(1)中, =表示拷贝初始化,通过使用(ctor)和通过隐式拷贝/移动临时将数字(11,22)转换为临时typesparticle定义的目标对象( protonelectron )。

相比之下,行(2)中的=parsing为对(op)的操作符调用,该操作符有效地返回其被调用的对象的副本( *this )。 constexpr东西允许在编译时使用这些variables,例如在模板声明中。 由于constexpr函数的限制,我们不能简单地在(op)函数中返回*this 。 另外, constexpr意味着const所有限制。

通过提供隐式转换运算符,可以在ParticleEnumtypes的行(2)中创build数组:

 // in struct particle constexpr operator ParticleEnum() { return m; } // in namespace particle_enum_detail ParticleEnum all_particles[] = { proton = 11, electron = 22 }; 

根据问题开始时提供的文章,我得出了一个解决scheme,其基础是假设你知道的是无效范围。

我真的想知道这是否是一个好的解决scheme。

首先,结束你枚举类似的东西: CAPI_END = 60 。 这将有助于交stream。 所以我的代码是:

 typedef enum { CAPI_SUBTYPE_NULL = 0, /* Null subtype. */ CAPI_SUBTYPE_DIAG_DFD = 1, /* Data Flow diag. */ CAPI_SUBTYPE_DIAG_ERD = 2, /* Entity-Relationship diag. */ CAPI_SUBTYPE_DIAG_STD = 3, /* State Transition diag. */ CAPI_SUBTYPE_DIAG_STC = 4, /* Structure Chart diag. */ CAPI_SUBTYPE_DIAG_DSD = 5, /* Data Structure diag. */ CAPI_SUBTYPE_SPEC_PROCESS = 6, /* Process spec. */ CAPI_SUBTYPE_SPEC_MODULE = 7, /* Module spec. */ CAPI_SUBTYPE_SPEC_TERMINATOR = 8, /* Terminator spec. */ CAPI_SUBTYPE_DD_ALL = 13, /* DD Entries (All). */ CAPI_SUBTYPE_DD_COUPLE = 14, /* DD Entries (Couples). */ CAPI_SUBTYPE_DD_DATA_AREA = 15, /* DD Entries (Data Areas). */ CAPI_SUBTYPE_DD_DATA_OBJECT = 16, /* DD Entries (Data Objects). */ CAPI_SUBTYPE_DD_FLOW = 17, /* DD Entries (Flows). */ CAPI_SUBTYPE_DD_RELATIONSHIP = 18, /* DD Entries (Relationships). */ CAPI_SUBTYPE_DD_STORE = 19, /* DD Entries (Stores). */ CAPI_SUBTYPE_DIAG_PAD = 35, /* Physical architecture diagram. */ CAPI_SUBTYPE_DIAG_BD = 36, /* Behaviour diagram. */ CAPI_SUBTYPE_DIAG_UCD = 37, /* UML Use case diagram. */ CAPI_SUBTYPE_DIAG_PD = 38, /* UML Package diagram. */ CAPI_SUBTYPE_DIAG_COD = 39, /* UML Collaboration diagram. */ CAPI_SUBTYPE_DIAG_SQD = 40, /* UML Sequence diagram. */ CAPI_SUBTYPE_DIAG_CD = 41, /* UML Class diagram. */ CAPI_SUBTYPE_DIAG_SCD = 42, /* UML State chart. */ CAPI_SUBTYPE_DIAG_ACD = 43, /* UML Activity chart. */ CAPI_SUBTYPE_DIAG_CPD = 44, /* UML Component diagram. */ CAPI_SUBTYPE_DIAG_DPD = 45, /* UML Deployment diagram. */ CAPI_SUBTYPE_DIAG_PFD = 47, /* Process flow diagram. */ CAPI_SUBTYPE_DIAG_HIER = 48, /* Hierarchy diagram. */ CAPI_SUBTYPE_DIAG_IDEF0 = 49, /* IDEF0 diagram. */ CAPI_SUBTYPE_DIAG_AID = 50, /* AID diagram. */ CAPI_SUBTYPE_DIAG_SAD = 51, /* SAD diagram. */ CAPI_SUBTYPE_DIAG_ASG = 59, /* ASG diagram. */ CAPI_END = 60 /* just to mark the end of your enum */ } CAPI_SUBTYPE_E ; CAPI_SUBTYPE_E& operator++(CAPI_SUBTYPE_E& capi) { const int ranges = 2; // you have 2 invalid ranges in your example int invalid[ranges][2] = {{8, 12}, {19, 34}}; // {min, max} (inclusive, exclusive) CAPI_SUBTYPE_E next = CAPI_SUBTYPE_NULL; for (int i = 0; i < ranges; i++) if ( capi >= invalid[i][0] && capi < invalid[i][1] ) { next = static_cast<CAPI_SUBTYPE_E>(invalid[i][1] + 1); break; } else { next = static_cast<CAPI_SUBTYPE_E>(capi + 1); } // if ( next > CAPI_END ) // throw an exception return capi = next; } int main() { for(CAPI_SUBTYPE_E i = CAPI_SUBTYPE_NULL; i < CAPI_END; ++i) cout << i << endl; cout << endl; } 

我只提供一个预增加运算符。 后期增量运算符稍后将被实现。

答案是“不,你不能迭代C ++ 03或C ++ 11中enum的元素”。

现在,您可以用编译时可以理解的方式来描述一组enum值。

 template<typename E, E... Es> struct TypedEnumList {}; typedef TypedEnumList< CAPI_SUBTYPE_E, CAPI_SUBTYPE_NULL, // etc // ... CAPI_SUBTYPE_DIAG_ASG > CAPI_SUBTYPE_E_LIST; 

它给你一个typesCAPI_SUBTYPE_E_LIST ,它封装了enum值的列表。

然后,我们可以很容易地用这些数组来填充数组:

  template<typename T, T... Es> std::array<T, sizeof...(Es)> GetRuntimeArray( TypedEnumList<T, Es... > ) { return { Es... }; } auto Capis = GetRuntimeArray( CAPI_SUBTYPE_E_LIST() ); 

如果你真的需要它。 但是这仅仅是能够为enum CAPI_SUBTYPE_E每个元素生成代码的一般情况的特殊情况enum CAPI_SUBTYPE_Eenum CAPI_SUBTYPE_E直接构buildfor循环。

有趣的是,使用兼容的C ++ 11编译器,我们可以编写代码来生成带有特定enum元素的CAPI_SUBTYPE_E_LIST ,如果这些元素实际上在使用SFINAE的CAPI_SUBTYPE_E中。 这很有用,因为我们可以使用我们可以支持的最新版本的API,并且如果我们编译的API更原始的话,它会自动降级(在编译时)。

为了演示这个技术,我将从一个玩具enum开始

 enum Foo { A = 0, /* B = 1 */ }; 

想象一下,在最现代的API版本中, B=1是未注释的,但在更原始的版本中并不存在。

 template<int index, typename EnumList, typename=void> struct AddElementN: AddElementN<index-1, EnumList> {}; template<typename EnumList> struct AddElementN<-1, EnumList, void> { typedef EnumList type; }; template<typename Enum, Enum... Es> struct AddElementN<0, TypedEnumList<Enum, Es...>, typename std::enable_if< Enum::A == Enum::A >::type >: AddElement<-1, TypedEnumList<Enum, A, Es...>> {}; template<typename Enum, Enum... Es> struct AddElementN<1, TypedEnumList<Enum, Es...>, typename std::enable_if< Enum::B == Enum::B >::type >: AddElement<0, TypedEnumList<Enum, B, Es...>> {}; // specialize this for your enum to call AddElementN: template<typename Enum> struct BuildTypedList; template<> struct BuildTypedList<CAPI_SUBTYPE_E>: AddElementN<1, TypedEnumList<CAPI_SUBTYPE_E>> {}; template<typename Enum> using TypedList = typename BuildTypedList<Enum>::type; 

现在,如果我写的是正确的, TypedList<CAPI_SUBTYPE_E>包含B iff B被定义为CAPI_SUBTYPE_E一个元素。 这使您可以针对多个版本的库进行编译,并根据库中的内容在enum元素列表中获取一组不同的元素。 你必须维护这个令人讨厌的样板(这可能会使macros或代码生成变得更容易)与enum元素的“最终”版本保持一致,但它应该在编译时自动处理以前的版本。

这可悲的是需要大量的维护工作。

最后,你要求这是dynamic的:唯一可行的方法是将第三方API包装在知道API版本的代码中,并公开一个不同的enum值缓冲区(我把它在一个std::vector )取决于什么版本的API。 然后,当你加载API时,你也加载这个帮助包装,然后使用上述技术来构buildenum的元素的集合,你迭代。

一些这样的样板可以使用一些可怕的macros来编写,比如使用__LINE__build立recursiontypes的索引来构build各种AddElementNtypes的SFINAE代码。 但是这太可怕了。

你不能遍历C ++中的任意enum 。 为了迭代,值应该放在某个容器中。 您可以使用如下所述的“枚举类”自动维护这样的容器: http : //www.drdobbs.com/when-enum-just-isnt-enough-enumeration-c/184403955http : //www.drdobbs.com/当-枚举刚刚心不是,足以枚举-C / 184403955

使用高阶macros

这是我们在项目中使用的技术。

概念:

这个想法是生成一个名为LISTING的macros,其中包含名称 – 值对的定义,并将另一个macros作为参数。 在下面的例子中,我定义了两个这样的助手macros。 'GENERATE_ENUM'生成枚举,'GENERATE_ARRAY'生成一个可迭代的数组。 当然这可以根据需要进行扩展。 我认为这个解决scheme给你最大的压力。 从概念上讲,它与iammilind的解决scheme非常相似。

例:

 // helper macros #define GENERATE_ENUM(key,value) \ key = value \ #define GENERATE_ARRAY(name,value) \ name \ // Since this is C++, I took the liberty to wrap everthing in a namespace. // This done mostly for aesthetic reasons, you don't have to if you don't want. namespace CAPI_SUBTYPES { // I define a macro containing the key value pairs #define LISTING(m) \ m(NONE, 0), /* Note: I can't use NULL here because it conflicts */ m(DIAG_DFD, 1), \ m(DIAG_ERD, 2), \ ... m(DD_ALL, 13), \ m(DD_COUPLE, 14), \ ... m(DIAG_SAD, 51), \ m(DIAG_ASG, 59), \ typedef enum { LISTING(GENERATE_ENUM) } Enum; const Enum At[] = { LISTING(GENERATE_ARRAY) }; const unsigned int Count = sizeof(At)/sizeof(At[0]); } 

用法:

现在在代码中你可以像这样引用枚举:

 CAPI_SUBTYPES::Enum eVariable = CAPI_SUBTYPES::DIAG_STD; 

你可以迭代枚举像这样:

 for (unsigned int i=0; i<CAPI_SUBTYPES::Count; i++) { ... CAPI_SUBTYPES::Enum eVariable = CAPI_SUBTYPES::At[i]; ... } 

注意:

如果内存为我服务,那么C ++ 11枚举就会存在于它们自己的命名空间中(比如在Java或者C#中),所以上面的用法是行不通的。 你必须像这个CAPI_SUBTYPES :: Enum :: FooBar一样引用枚举值。

稍微清楚一点(???)一点推进预处理。

你通过一个序列来定义你的枚举

 #define CAPI_SUBTYPE_E_Sequence \ (CAPI_SUBTYPE_NULL)(0) \ (CAPI_SUBTYPE_DIAG_DFD)(1) ... 

那么你可以自动化(通过macros)枚举的声明,

 DECL_ENUM(CAPI_SUBTYPE_E) ; 

索引它的表

 DECL_ENUM_TABLE(CAPI_SUBTYPE_E); 

表格的枚举数/大小

 ENUM_SIZE(CAPI_SUBTYPE_E) 

并访问它:

 ITER_ENUM_i(i,CAPI_SUBTYPE_E) 

这是全文。

 #include <boost/preprocessor.hpp> // define your enum as (name)(value) sequence #define CAPI_SUBTYPE_E_Sequence \ (CAPI_SUBTYPE_NULL)(0) /* Null subtype. */ \ (CAPI_SUBTYPE_DIAG_DFD)(1) /* Data Flow diag. */ \ (CAPI_SUBTYPE_DIAG_ERD)(2) /* Entity-Relationship diag. */ \ (CAPI_SUBTYPE_DIAG_DSD)(5) /* Data Structure diag. */ \ (CAPI_SUBTYPE_DD_ALL)(13) /* DD Entries (All). */ // # enums #define ENUM_SIZE(name) \ BOOST_PP_DIV(BOOST_PP_SEQ_SIZE(BOOST_PP_CAT(name,_Sequence)),2) #define ENUM_NAME_N(N,seq) BOOST_PP_SEQ_ELEM(BOOST_PP_MUL(N,2),seq) #define ENUM_VALUE_N(N,seq) BOOST_PP_SEQ_ELEM(BOOST_PP_INC(BOOST_PP_MUL(N,2)),seq) // declare Nth enum #define DECL_ENUM_N(Z,N,seq) \ BOOST_PP_COMMA_IF(N) ENUM_NAME_N(N,seq) = ENUM_VALUE_N(N,seq) // declare whole enum #define DECL_ENUM(name) \ typedef enum { \ BOOST_PP_REPEAT( ENUM_SIZE(name) , DECL_ENUM_N , BOOST_PP_CAT(name,_Sequence) ) \ } name DECL_ENUM(CAPI_SUBTYPE_E) ; // declare Nth enum value #define DECL_ENUM_TABLE_N(Z,N,seq) \ BOOST_PP_COMMA_IF(N) ENUM_NAME_N(N,seq) // declare table #define DECL_ENUM_TABLE(name) \ static const name BOOST_PP_CAT(name,_Table) [ENUM_SIZE(name)] = { \ BOOST_PP_REPEAT( ENUM_SIZE(name) , DECL_ENUM_TABLE_N , BOOST_PP_CAT(name,_Sequence) ) \ } DECL_ENUM_TABLE(CAPI_SUBTYPE_E); #define ITER_ENUM_i(i,name) BOOST_PP_CAT(name,_Table) [i] // demo // outputs : [0:0] [1:1] [2:2] [3:5] [4:13] #include <iostream> int main() { for (int i=0; i<ENUM_SIZE(CAPI_SUBTYPE_E) ; i++) std::cout << "[" << i << ":" << ITER_ENUM_i(i,CAPI_SUBTYPE_E) << "] "; return 0; } // bonus : check enums are unique and in-order #include <boost/preprocessor/stringize.hpp> #include <boost/static_assert.hpp> #define CHECK_ENUM_N(Z,N,seq) \ BOOST_PP_IF( N , \ BOOST_STATIC_ASSERT_MSG( \ ENUM_VALUE_N(BOOST_PP_DEC(N),seq) < ENUM_VALUE_N(N,seq) , \ BOOST_PP_STRINGIZE( ENUM_NAME_N(BOOST_PP_DEC(N),seq) ) " not < " BOOST_PP_STRINGIZE( ENUM_NAME_N(N,seq) ) ) \ , ) ; #define CHECK_ENUM(name) \ namespace { void BOOST_PP_CAT(check_enum_,name) () { \ BOOST_PP_REPEAT( ENUM_SIZE(name) , CHECK_ENUM_N , BOOST_PP_CAT(name,_Sequence) ) } } // enum OK CHECK_ENUM(CAPI_SUBTYPE_E) #define Bad_Enum_Sequence \ (one)(1)\ (five)(5)\ (seven)(7)\ (three)(3) // enum not OK : enum_iter.cpp(81): error C2338: seven not < three CHECK_ENUM(Bad_Enum) 

不涉及macros和几乎没有运行时开销的解决scheme的开始:

 #include <iostream> #include <utility> #include <boost/mpl/vector.hpp> #include <boost/mpl/find.hpp> template<int v> using has_value = std::integral_constant<int, v>; template<class...EnumValues> struct better_enum { static constexpr size_t size = sizeof...(EnumValues); using value_array = int[size]; static const value_array& values() { static const value_array _values = { EnumValues::value... }; return _values; } using name_array = const char*[size]; static const name_array& names() { static const name_array _names = { EnumValues::name()... }; return _names; } using enum_values = boost::mpl::vector<EnumValues...>; struct iterator { explicit iterator(size_t i) : index(i) {} const char* name() const { return names()[index]; } int value() const { return values()[index]; } operator int() const { return value(); } void operator++() { ++index; } bool operator==(const iterator& it) const { return index == it.index; } bool operator!=(const iterator& it) const { return index != it.index; } const iterator& operator*() const { return *this; } private: size_t index; }; friend std::ostream& operator<<(std::ostream& os, const iterator& iter) { os << "{ " << iter.name() << ", " << iter.value() << " }"; return os; } template<class EnumValue> static iterator find() { using iter = typename boost::mpl::find<enum_values, EnumValue>::type; static_assert(iter::pos::value < size, "attempt to find a value which is not part of this enum"); return iterator { iter::pos::value }; } static iterator begin() { return iterator { 0 }; } static iterator end() { return iterator { size }; } }; struct Pig : has_value<0> { static const char* name() { return "Pig";} }; struct Dog : has_value<7> { static const char* name() { return "Dog";} }; struct Cat : has_value<100> { static const char* name() { return "Cat";} }; struct Horse : has_value<90> { static const char* name() { return "Horse";} }; struct Monkey : has_value<1000> { static const char* name() { return "Monkey";} }; using animals = better_enum< Pig, Dog, Cat, Horse >; using namespace std; auto main() -> int { cout << "size : " << animals::size << endl; for (auto v : animals::values()) cout << v << endl; for (auto v : animals::names()) cout << v << endl; cout << "full iteration:" << endl; for (const auto& i : animals()) { cout << i << endl; } cout << "individials" << endl; auto animal = animals::find<Dog>(); cout << "found : " << animal << endl; while (animal != animals::find<Horse>()) { cout << animal << endl; ++animal; } // will trigger the static_assert auto xx = animals::find<Monkey>(); return 0; } 

输出:

 size : 4 0 7 100 90 Pig Dog Cat Horse full iteration: { Pig, 0 } { Dog, 7 } { Cat, 100 } { Horse, 90 } individials found : { Dog, 7 } { Dog, 7 } { Cat, 100 } 

把它们放到一个数组或其他容器中,然后重复。 如果修改枚举,则必须更新将它们放入容器中的代码。

由于枚举不允许迭代,所以必须创build枚举的一个替代表示及其值的范围。

我将采取的方法是embedded在类中的简单表格查找。 问题是,当API用新的条目修改它的枚举时,你还需要更新这个类的构造函数。

我将使用的简单类将由一个构造函数构成,以及用于遍历表的一些方法。 由于在添加项目时还想知道表大小是否存在问题,因此可以使用assert ()macros,该macros将在debugging模式下发出assert() 。 在下面的源代码示例中,我使用预处理器来testing是否进行debugging编译,以及是否已经包含了assert,从而为基本的一致性检查提供机制。

我借鉴了PJ Plauger在他的“标准C库”一书中看到的一个想法,使用一个简单的查找表进行ANSI字符操作,在该操作中字符用于索引表。

要使用这个类,你可以做类似下面的事情,它使用for循环遍历表中的一组值。 在循环体内,你可以做任何你想要做的枚举值。

 CapiEnum myEnum; for (CAPI_SUBTYPE_E jj = myEnum.Begin(); !myEnum.End(); jj = myEnum.Next()) { // do stuff with the jj enum value } 

由于这个类枚举的价值,我已经任意select返回CAPI_SUBTYPE_NULL的价值在那些我们已经达到了枚举的结束的情况下。 因此,在查找表错误的情况下,返回值在有效范围内,但不能依赖。 那么应该检查End()方法,看是否已经达到了迭代的结束。 在完成对象的构build之后,可以检查m_bTableError数据成员以查看构造过程中是否出现错误。

下面是该类的源代码示例。 你将需要更新构造函数的API的枚举值,因为他们改变。 不幸的是,没有太多可以做自动检查一个更新枚举,但是我们确实在debugging编译的地方进行testing,检查表是否足够大,并且被放入表中的枚举的值是在桌子大小的范围。

 class CapiEnum { public: CapiEnum (void); // constructor CAPI_SUBTYPE_E Begin (void); // method to call to begin an iteration CAPI_SUBTYPE_E Next (void); // method to get the next in the series of an iteration bool End (void); // method to indicate if we have reached the end or not bool Check (CAPI_SUBTYPE_E value); // method to see if value specified is in the table bool m_TableError; private: static const int m_TableSize = 256; // set the lookup table size static const int m_UnusedTableEntry = -1; int m_iIterate; bool m_bEndReached; CAPI_SUBTYPE_E m_CapiTable[m_TableSize]; }; #if defined(_DEBUG) #if defined(assert) #define ADD_CAPI_ENUM_ENTRY(capi) (((capi) < m_TableSize && (capi) > m_UnusedTableEntry) ? (m_CapiTable[(capi)] = (capi)) : assert(((capi) < m_TableSize) && ((capi) > m_UnusedTableEntry))) #else #define ADD_CAPI_ENUM_ENTRY(capi) (((capi) < m_TableSize && (capi) > m_UnusedTableEntry) ? (m_CapiTable[(capi)] = (capi)) : (m_TableError = true)) #endif #else #define ADD_CAPI_ENUM_ENTRY(capi) (m_CapiTable[(capi)] = (capi)) #endif CapiEnum::CapiEnum (void) : m_bEndReached(true), m_iIterate(0), m_TableError(false) { for (int iLoop = 0; iLoop < m_TableSize; iLoop++) m_CapiTable[iLoop] = static_cast <CAPI_SUBTYPE_E> (m_UnusedTableEntry); ADD_CAPI_ENUM_ENTRY(CAPI_SUBTYPE_NULL); // ..... ADD_CAPI_ENUM_ENTRY(CAPI_SUBTYPE_DIAG_ASG); } CAPI_SUBTYPE_E CapiEnum::Begin (void) { m_bEndReached = false; for (m_iIterate = 0; m_iIterate < m_TableSize; m_iIterate++) { if (m_CapiTable[m_iIterate] > m_UnusedTableEntry) return m_CapiTable[m_iIterate]; } m_bEndReached = true; return CAPI_SUBTYPE_NULL; } CAPI_SUBTYPE_E CapiEnum::Next (void) { if (!m_bEndReached) { for (m_iIterate++; m_iIterate < m_TableSize; m_iIterate++) { if (m_CapiTable[m_iIterate] > m_UnusedTableEntry) return m_CapiTable[m_iIterate]; } } m_bEndReached = true; return CAPI_SUBTYPE_NULL; } bool CapiEnum::End (void) { return m_bEndReached; } bool CapiEnum::Check (CAPI_SUBTYPE_E value) { return (value >= 0 && value < m_TableSize && m_CapiTable[value] > m_UnusedTableEntry); } 

如果你喜欢,你可以添加一个额外的方法来检索迭代的当前值。 请注意,Current()方法不是递增到下一个,而是使用迭代索引当前所在的位置,并从当前位置开始search。 所以如果当前位置是一个有效的值,它只是返回它,否则会find第一个有效的值。 或者,您可以只返回索引指向的当前表值,如果该值无效,则设置错误指示符。

 CAPI_SUBTYPE_E CapiEnum::Current (void) { if (!m_bEndReached) { for (m_iIterate; m_iIterate < m_TableSize; m_iIterate++) { if (m_CapiTable[m_iIterate] > m_UnusedTableEntry) return m_CapiTable[m_iIterate]; } } m_bEndReached = true; return CAPI_SUBTYPE_NULL; } 

这里还有一个方法。 一个好处是,如果您在switch省略枚举值,编译器可能会警告您:

 template<typename T> void IMP_Apply(const int& pSubtype, T& pApply) { switch (pSubtype) { case CAPI_SUBTYPE_NULL : case CAPI_SUBTYPE_DIAG_DFD : case CAPI_SUBTYPE_DIAG_ERD : case CAPI_SUBTYPE_DIAG_STD : case CAPI_SUBTYPE_DIAG_STC : case CAPI_SUBTYPE_DIAG_DSD : case CAPI_SUBTYPE_SPEC_PROCESS : case CAPI_SUBTYPE_SPEC_MODULE : case CAPI_SUBTYPE_SPEC_TERMINATOR : case CAPI_SUBTYPE_DD_ALL : case CAPI_SUBTYPE_DD_COUPLE : case CAPI_SUBTYPE_DD_DATA_AREA : case CAPI_SUBTYPE_DD_DATA_OBJECT : case CAPI_SUBTYPE_DD_FLOW : case CAPI_SUBTYPE_DD_RELATIONSHIP : case CAPI_SUBTYPE_DD_STORE : case CAPI_SUBTYPE_DIAG_PAD : case CAPI_SUBTYPE_DIAG_BD : case CAPI_SUBTYPE_DIAG_UCD : case CAPI_SUBTYPE_DIAG_PD : case CAPI_SUBTYPE_DIAG_COD : case CAPI_SUBTYPE_DIAG_SQD : case CAPI_SUBTYPE_DIAG_CD : case CAPI_SUBTYPE_DIAG_SCD : case CAPI_SUBTYPE_DIAG_ACD : case CAPI_SUBTYPE_DIAG_CPD : case CAPI_SUBTYPE_DIAG_DPD : case CAPI_SUBTYPE_DIAG_PFD : case CAPI_SUBTYPE_DIAG_HIER : case CAPI_SUBTYPE_DIAG_IDEF0 : case CAPI_SUBTYPE_DIAG_AID : case CAPI_SUBTYPE_DIAG_SAD : case CAPI_SUBTYPE_DIAG_ASG : /* do something. just `applying`: */ pApply(static_cast<CAPI_SUBTYPE_E>(pSubtype)); return; } std::cout << "Skipped: " << pSubtype << '\n'; } template<typename T> void Apply(T& pApply) { const CAPI_SUBTYPE_E First(CAPI_SUBTYPE_NULL); const CAPI_SUBTYPE_E Last(CAPI_SUBTYPE_DIAG_ASG); for (int idx(static_cast<int>(First)); idx <= static_cast<int>(Last); ++idx) { IMP_Apply(idx, pApply); } } int main(int argc, const char* argv[]) { class t_apply { public: void operator()(const CAPI_SUBTYPE_E& pSubtype) const { std::cout << "Apply: " << static_cast<int>(pSubtype) << '\n'; } }; t_apply apply; Apply(apply); return 0; } 

I'm using this type of constructions to define my own enums:

 #include <boost/unordered_map.hpp> namespace enumeration { struct enumerator_base : boost::noncopyable { typedef boost::unordered_map<int, std::string> kv_storage_t; typedef kv_storage_t::value_type kv_type; typedef std::set<int> entries_t; typedef entries_t::const_iterator iterator; typedef entries_t::const_iterator const_iterator; kv_storage_t const & kv() const { return storage_; } const char * name(int i) const { kv_storage_t::const_iterator it = storage_.find(i); if(it != storage_.end()) return it->second.c_str(); return "empty"; } iterator begin() const { return entries_.begin(); } iterator end() const { return entries_.end(); } iterator begin() { return entries_.begin(); } iterator end() { return entries_.end(); } void register_e(int val, std::string const & desc) { storage_.insert(std::make_pair(val, desc)); entries_.insert(val); } protected: kv_storage_t storage_; entries_t entries_; }; template<class T> struct enumerator; template<class D> struct enum_singleton : enumerator_base { static enumerator_base const & instance() { static D inst; return inst; } }; } #define QENUM_ENTRY(K, V, N) K, N register_e((int)K, V); #define QENUM_ENTRY_I(K, I, V, N) K = I, N register_e((int)K, V); #define QBEGIN_ENUM(NAME, C) \ enum NAME \ { \ C \ } \ }; \ } \ #define QEND_ENUM(NAME) \ }; \ namespace enumeration \ { \ template<> \ struct enumerator<NAME>\ : enum_singleton< enumerator<NAME> >\ { \ enumerator() \ { QBEGIN_ENUM(test_t, QENUM_ENTRY(test_entry_1, "number uno", QENUM_ENTRY_I(test_entry_2, 10, "number dos", QENUM_ENTRY(test_entry_3, "number tres", QEND_ENUM(test_t))))) int _tmain(int argc, _TCHAR* argv[]) { BOOST_FOREACH(int x, enumeration::enumerator<test_t>::instance()) std::cout << enumeration::enumerator<test_t>::instance().name(x) << "=" << x << std::endl; return 0; } 

Also you can replace storage_ type to boost::bimap to have bidirectional correspondance int <==> string

There are a lot of answers to this question already, but most of them are either very complicated or inefficient in that they don't directly address the requirement of iterating over an enum with gaps. Everyone so far has said that this is not possible, and they are sort of correct in that there is no language feature to allow you to do this. That certainly does not mean you can't, and as we can see by all the answers so far, there are many different ways to do it. Here is my way, based on the enum you have provided and the assumption that it's structure won't change much. Of course this method can be adapted as needed.

 typedef enum { CAPI_SUBTYPE_NULL = 0, /* Null subtype. */ CAPI_SUBTYPE_DIAG_DFD = 1, /* Data Flow diag. */ CAPI_SUBTYPE_DIAG_ERD = 2, /* Entity-Relationship diag. */ CAPI_SUBTYPE_DIAG_STD = 3, /* State Transition diag. */ CAPI_SUBTYPE_DIAG_STC = 4, /* Structure Chart diag. */ CAPI_SUBTYPE_DIAG_DSD = 5, /* Data Structure diag. */ CAPI_SUBTYPE_SPEC_PROCESS = 6, /* Process spec. */ CAPI_SUBTYPE_SPEC_MODULE = 7, /* Module spec. */ CAPI_SUBTYPE_SPEC_TERMINATOR = 8, /* Terminator spec. */ CAPI_SUBTYPE_DD_ALL = 13, /* DD Entries (All). */ CAPI_SUBTYPE_DD_COUPLE = 14, /* DD Entries (Couples). */ CAPI_SUBTYPE_DD_DATA_AREA = 15, /* DD Entries (Data Areas). */ CAPI_SUBTYPE_DD_DATA_OBJECT = 16, /* DD Entries (Data Objects). */ CAPI_SUBTYPE_DD_FLOW = 17, /* DD Entries (Flows). */ CAPI_SUBTYPE_DD_RELATIONSHIP = 18, /* DD Entries (Relationships). */ CAPI_SUBTYPE_DD_STORE = 19, /* DD Entries (Stores). */ CAPI_SUBTYPE_DIAG_PAD = 35, /* Physical architecture diagram. */ CAPI_SUBTYPE_DIAG_BD = 36, /* Behaviour diagram. */ CAPI_SUBTYPE_DIAG_UCD = 37, /* UML Use case diagram. */ CAPI_SUBTYPE_DIAG_PD = 38, /* UML Package diagram. */ CAPI_SUBTYPE_DIAG_COD = 39, /* UML Collaboration diagram. */ CAPI_SUBTYPE_DIAG_SQD = 40, /* UML Sequence diagram. */ CAPI_SUBTYPE_DIAG_CD = 41, /* UML Class diagram. */ CAPI_SUBTYPE_DIAG_SCD = 42, /* UML State chart. */ CAPI_SUBTYPE_DIAG_ACD = 43, /* UML Activity chart. */ CAPI_SUBTYPE_DIAG_CPD = 44, /* UML Component diagram. */ CAPI_SUBTYPE_DIAG_DPD = 45, /* UML Deployment diagram. */ CAPI_SUBTYPE_DIAG_PFD = 47, /* Process flow diagram. */ CAPI_SUBTYPE_DIAG_HIER = 48, /* Hierarchy diagram. */ CAPI_SUBTYPE_DIAG_IDEF0 = 49, /* IDEF0 diagram. */ CAPI_SUBTYPE_DIAG_AID = 50, /* AID diagram. */ CAPI_SUBTYPE_DIAG_SAD = 51, /* SAD diagram. */ CAPI_SUBTYPE_DIAG_ASG = 59 /* ASG diagram. */ } CAPI_SUBTYPE_E; struct ranges_t { int start; int end; }; ranges_t ranges[] = { {CAPI_SUBTYPE_NULL, CAPI_SUBTYPE_NULL}, {CAPI_SUBTYPE_DIAG_DFD, CAPI_SUBTYPE_DIAG_DSD}, {CAPI_SUBTYPE_SPEC_PROCESS, CAPI_SUBTYPE_SPEC_TERMINATOR}, {CAPI_SUBTYPE_DD_ALL, CAPI_SUBTYPE_DD_STORE}, {CAPI_SUBTYPE_DIAG_PAD, CAPI_SUBTYPE_DIAG_SAD}, {CAPI_SUBTYPE_DIAG_ASG, CAPI_SUBTYPE_DIAG_ASG}, }; int numRanges = sizeof(ranges) / sizeof(*ranges); for( int rangeIdx = 0; rangeIdx < numRanges; ++rangeIdx ) { for( int enumValue = ranges[rangeIdx].start; enumValue <= ranges[rangeIdx].end; ++enumValue ) { processEnumValue( enumValue ); } } 

或类似的规定。

The only real 'solution' I finally came up with to solve this problem is to create a pre-run script that reads the c/c++ file(s) containing the enums and generates a class file that has a list of all the enums as vectors. This is very much the same way Visual Studio supports T4 Templates . In the .Net world it's pretty common practice but since I can't work in that environment I was forced to do it this way.

The script I wrote is in Ruby, but you could do it in whatever language. If anyone wants the source script, I uploaded it here . It's by no means a perfect script but it fit the bill for my project. I encourage anyone to improve on it and give tips here.