迭代非增量枚举

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)

  • @RichardChambers是的,你很干净.我只是不喜欢将枚举用作索引(例如,遍历数组).默认值会激励我们去做.我的意思是这样的代码`for(int i = 0; ENUM_FIRST; i <ENUM_LAST; ++ i)do_smth(data [i])`.此外,我不喜欢程序员分配枚举值时,因为标准不会阻止我们复制值.因此,"值是随机的"我的意思是"尽管`enum`的默认值是由标准保证的,我更愿意考虑枚举值是随机的". (4认同)
  • 我好奇你的意思是"表面上枚举值在每次编译中都是随机的",因为标准是默认值被赋值给以零值开头的枚举,除非指定了一个值.见http://www.cprogramming.com/tutorial/enum.html和http://www.learncpp.com/cpp-tutorial/45-enumerated-types/ (3认同)

dyp*_*dyp 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 << ", ";
    }
}
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)