在您提出问题之前,我已经looked和looked了解了这一点,并且无法找到可靠的答案。
我需要能够动态迭代具有非增量值的枚举,例如:
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版本如何,都希望能够迭代超过这些价值观。
任何方向都表示赞赏。
答案 0 :(得分:16)
使用C ++,迭代枚举的唯一方法是将它们存储在一个数组中并迭代它。主要的挑战是如何在enum
声明和数组声明中跟踪相同的顺序?
您可以自动化在enum
以及数组中订购它们的方式。我觉得这是一个不错的方式:
// 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. */
现在你在枚举声明和数组声明中#include
这个文件都有宏重新定义:
// Enum.h
typedef enum {
#define NAME_VALUE(NAME,VALUE) NAME = VALUE
#include"CAPI_SUBTYPE_E_list.h"
#undef NAME_VALUE
}CAPI_SUBTYPE_E;
将相同的文件放在具有其他宏定义的数组中:
// 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)
答案 1 :(得分:11)
它比C ++实践更棘手,更C,但你可以使用X宏。这非常难看,你需要保持正确的TABLE顺序。在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
}
答案 2 :(得分:7)
我同意已经给出的陈述,即如果不改变或复制enum
的定义,这是不可能的。但是,在C ++ 11中(甚至可能是C ++ 03?),您可以提供一种语法,您需要做的就是将字面上的枚举器定义复制并粘贴到enum
中。一个宏。只要每个枚举器都有一个明确的定义(使用=
)。
编辑:即使并非每个枚举数都有明确的定义,您也可以将其展开,但在这种情况下不应该这样做。
我曾经为一些物理学家开发过这个,所以这个例子是关于粒子的。
// 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 << ", ";
}
}
宏产生(有效):
namespace particle_enum_detail
{
// definition of a type and some constants
constexpr ParticleEnum all_particles[] = {
PROTON,
ELECTRON,
MUON
};
}
using particle_enum_detail::all_particles;
#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
注意:类型iterable_enum_
也可以在宏外部定义。
这个想法是在宏调用中允许像proton = 11, electron = 12
这样的语法。这对于任何类型的声明都非常容易,但是存储名称会产生问题:
#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`
这还没有解决我们的问题,它只是说明如果我们不需要枚举器的自动增量功能,我们可以通过常量列表轻松替换枚举。
现在,运算符重载的语法技巧:
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)行中,=
指定了复制初始化,这是通过将数字(11
,22
)转换为临时类型{来完成的。 {1}}通过使用(ctor)并通过隐式定义的ctor将临时值复制/移动到目标对象(particle
,proton
)。
相反,第(2)行中的electron
被解析为对(op)的操作符调用,它有效地返回被调用它的对象的副本(=
)。 *this
内容允许在编译时使用这些变量,例如在模板声明中。由于constexpr
函数的限制,我们不能简单地在(op)函数中返回constexpr
。此外,*this
表示constexpr
的所有限制。
通过提供隐式转换运算符,您可以在const
类型的第(2)行中创建数组:
ParticleEnum
答案 3 :(得分:3)
根据问题开头给出的文章,我得出了一个基于假设你知道invalids范围的解决方案。
我真的想知道这是否是一个很好的解决方案。
首先,结束这样的事情:CAPI_END = 60
。这将有助于互动。所以我的代码是:
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;
}
我只提供一个预增量运算符。后增量运算符可以在以后实现。
答案 4 :(得分:3)
答案是“不,你不能在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;
为您提供了一个类型CAPI_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
的每个元素生成代码的更一般情况的特例 - 不需要直接构建for
循环。
有趣的是,使用兼容的C ++ 11编译器,我们可以编写代码,如果这些元素实际上在CAPI_SUBTYPE_E_LIST
中使用SFINAE,则会生成具有特定enum
元素的CAPI_SUBTYPE_E
。这很有用,因为我们可以使用我们可以支持的API的最新版本,并且如果我们编译的API更原始,它会自动降级(在编译时)。
为了演示这项技巧,我将从玩具enum
enum Foo { A = 0, /* B = 1 */ };
想象一下,B=1
在最现代版本的API中取消注释,但在更原始的版本中不存在。
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
元素列表中获取一组不同的元素。您必须针对enum
s元素的“最终”版本维护那个令人讨厌的样板(可能通过宏或代码生成更容易),但它应该在编译时自动处理以前的版本。
这可遗憾地需要大量的维护工作。
最后,您要求这是动态的:动态的唯一实用方法是将第三方API包装在知道API版本的代码中,并公开{{1}的不同缓冲区}值(我将它放在enum
中),具体取决于API的版本。然后当你加载API时,你也加载这个帮助器包装器,然后使用上面的技术来构建你迭代的std::vector
元素集。
使用enum
索引递归类型,可以使用一些可怕的宏(例如构建各种AddElementN
类型SFINAE代码的宏)来更容易地编写这些样板文件。但那太可怕了。
答案 5 :(得分:3)
稍微清晰一点(???)并进行一些增强预处理。
您可以按顺序
定义枚举#define CAPI_SUBTYPE_E_Sequence \
(CAPI_SUBTYPE_NULL)(0) \
(CAPI_SUBTYPE_DIAG_DFD)(1) ...
然后你可以自动(通过宏)枚举的声明,
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)
答案 6 :(得分:2)
您无法在C ++中迭代任意enum
。对于迭代,值应放在某个容器中。您可以使用“枚举类”自动维护此类容器,如下所述:http://www.drdobbs.com/when-enum-just-isnt-enough-enumeration-c/184403955http://www.drdobbs.com/when-enum-just-isnt-enough-enumeration-c/184403955
答案 7 :(得分:2)
这是我们在项目中使用的技术。
<强>概念强>
这个想法是生成一个名为LISTING的宏,它包含名称 - 值对的定义,它将另一个宏作为参数。在下面的例子中,我定义了两个这样的辅助宏。 'GENERATE_ENUM'生成枚举,'GENERATE_ARRAY'生成可迭代数组。当然,这可以根据需要进行扩展。我认为这个解决方案可以让您获得最大的收益。 从概念上讲,它与iammilind's solution非常相似。
示例:强>
// 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 这样的枚举值。
答案 8 :(得分:1)
解决方案的开头不涉及宏,并且(几乎)没有运行时开销:
#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 }
答案 9 :(得分:0)
将它们放入数组或其他容器中并迭代它。如果修改枚举,则必须更新将它们放入容器中的代码。
答案 10 :(得分:0)
这是另一种方法。一个好处是,如果省略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;
}
答案 11 :(得分:0)
由于枚举不允许迭代,您必须创建枚举及其值范围的替代表示。
我将采用的方法是嵌入在类中的简单表查找。问题是,当API使用新条目修改其枚举时,您还需要更新此类的构造函数。
我将使用的简单类将包含一个构造函数,用于构建表以及一些迭代表的方法。由于您还想知道在添加项目时表格大小是否存在问题,您可以使用在调试模式下发出assert ()
的{{1}}宏。在下面的源代码示例中,我使用预处理器来测试是否编译调试以及是否包含assert,以便提供基本一致性检查的机制。
我借用P. J. Plauger在他的书“标准C库”中看到的一个想法,即使用简单的ANSI字符操作查找表,其中字符用于索引表格。
要使用此类,您将执行类似以下操作,使用assert()
循环来迭代表中的值集。在循环体内,您可以使用枚举值执行任何操作。
for
由于这个类枚举了这些值,因此在我们已经到达枚举结束的情况下,我已经任意选择返回CapiEnum myEnum;
for (CAPI_SUBTYPE_E jj = myEnum.Begin(); !myEnum.End(); jj = myEnum.Next()) {
// do stuff with the jj enum value
}
的值。 因此,在表查找错误的情况下返回值在有效范围内,但不能依赖它。因此,应该检查CAPI_SUBTYPE_NULL
方法以查看是否已达到迭代的结束。在构建对象之后,可以检查End()
数据成员以查看构造期间是否存在错误。
该类的源示例如下。 您需要在API更改时使用API的枚举值更新构造函数。不幸的是,没有太多可以自动检查更新枚举但是我们确实在一个debug编译,用于检查表是否足够大,并且放入表中的枚举值是否在表大小的范围内。
m_bTableError
如果您愿意,可以添加其他方法来检索迭代的当前值。请注意,Current()方法不是递增到下一个,而是使用迭代索引当前所处的任何内容,并从当前位置开始搜索。因此,如果当前位置是有效值,它只返回它,否则它将找到第一个有效值。或者,您可以使它只返回索引指向的当前表值,如果值无效,则设置错误指示符。
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);
}
答案 12 :(得分:0)
我正在使用这种类型的结构来定义我自己的枚举:
#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;
}
此外,您可以将storage_
类型替换为boost::bimap
以使其具有双向对应关系int&lt; ==&gt;串
答案 13 :(得分:0)
这个问题已经有很多答案,但是大多数答案要么非常复杂,要么效率低下,因为它们没有直接解决迭代带有间隙的枚举的要求。到目前为止,每个人都说这是不可能的,而且它们是正确的,因为没有语言功能可以让你这样做。这当然不意味着你不能,而且正如我们从目前为止的所有答案中看到的那样,有很多不同的方法可以做到这一点。这是我的方式,基于你提供的枚举,并假设它的结构不会有太大变化。当然,这种方法可以根据需要进行调整。
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 );
}
}
或类似的东西。
答案 14 :(得分:0)
我最终提出解决此问题的唯一真正的“解决方案”是创建一个预运行脚本,该脚本读取包含枚举的c / c ++文件并生成一个包含所有列表的类文件枚举作为向量。这与Visual Studio支持T4 Templates的方式非常相似。在.Net世界中,这是很常见的做法,但由于我不能在那种环境中工作,所以我不得不这样做。
我写的脚本是用Ruby编写的,但你可以用任何语言编写。如果有人想要源脚本,我上传了here。它绝不是一个完美的脚本,但它符合我项目的费用。我鼓励任何人改进它并在这里给出提示。