Chr*_*les 38 c++ enums iterator
在你问之前,我已经在SO上寻找并寻找了这个,但是找不到可靠的答案.
我需要能够动态迭代具有非增量值的枚举,例如:
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 ;
Run Code Online (Sandbox Code Playgroud)
我希望能够这样做的原因是因为枚举是在API中给出的(显然我无法改变),并且无论API版本如何,都希望能够迭代这些值.
任何方向表示赞赏.
iam*_*ind 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. */
Run Code Online (Sandbox Code Playgroud)
现在你#include在enum声明和数组声明中的这个文件都有宏重新定义:
// Enum.h
typedef enum {
#define NAME_VALUE(NAME,VALUE) NAME = VALUE
#include"CAPI_SUBTYPE_E_list.h"
#undef NAME_VALUE
}CAPI_SUBTYPE_E;
Run Code Online (Sandbox Code Playgroud)
并为与其他宏定义的数组放置相同的文件:
// 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
};
Run Code Online (Sandbox Code Playgroud)
现在在C++ 03中迭代:
for(unsigned int i = 0, size = sizeof(CAPI_SUBTYPE_E_Array)/sizeof(CAPI_SUBTYPE_E_Array[0]);
i < size; ++i)
Run Code Online (Sandbox Code Playgroud)
或者在C++ 11中简单:
for(auto i : CAPI_SUBTYPE_E_Array)
Run Code Online (Sandbox Code Playgroud)
fas*_*ked 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
}
Run Code Online (Sandbox Code Playgroud)
我同意已经发表的声明,如果不改变或复制的定义,这是不可能的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 << ", ";
}
}
Run Code Online (Sandbox Code Playgroud)
宏观收益率(有效):
namespace particle_enum_detail
{
// definition of a type and some constants
constexpr ParticleEnum all_particles[] = {
PROTON,
ELECTRON,
MUON
};
}
using particle_enum_detail::all_particles;
Run Code Online (Sandbox Code Playgroud)
#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
Run Code Online (Sandbox Code Playgroud)
注意:类型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);
Run Code Online (Sandbox Code Playgroud)
收益率:
enum my_enum { proton = 11, electron = 22 }; // would be OK
my_enum all[] = { proton = 11, electron = 22 }; // cannot assign to enumerator
Run Code Online (Sandbox Code Playgroud)
与许多语法技巧一样,运算符重载提供了一种克服此问题的方法; 但赋值运算符必须是成员函数 - 枚举不是类.那么为什么不使用一些常量对象而不是枚举呢?
enum my_enum { proton = 11, electron = 22 };
// alternatively
constexpr int proton = 11, electron = 12;
// the `constexpr` here is equivalent to a `const`
Run Code Online (Sandbox Code Playgroud)
这还没有解决我们的问题,它只是表明如果我们不需要枚举器的自动增量功能,我们可以通过常量列表轻松替换枚举.
现在,运算符重载的语法技巧:
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)
Run Code Online (Sandbox Code Playgroud)
诀窍是,管线(1)=表示一个复制初始化,这是通过将数(完成的11,22)到临时类型的particle使用(构造函数)和复制/经由隐式定义的构造函数,以移动临时目标对象(proton,electron).
相反,第=(2)行被解析为对(op)的操作符调用,它有效地返回被调用它的对象的副本(*this).这些constexpr东西允许在编译时使用这些变量,例如在模板声明中.由于constexpr功能的限制,我们不能简单地返回*this(op)函数.此外,constexpr暗示所有限制const.
通过提供隐式转换运算符,您可以在类型的第(2)行中创建数组ParticleEnum:
// in struct particle
constexpr operator ParticleEnum() { return m; }
// in namespace particle_enum_detail
ParticleEnum all_particles[] = { proton = 11, electron = 22 };
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
10275 次 |
| 最近记录: |