首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >迭代非增量Enum

迭代非增量Enum
EN

Stack Overflow用户
提问于 2013-05-16 20:05:48
回答 15查看 11.5K关注 0票数 39

在您问之前,我已经有了看上去看上去在这上面,并且找不到一个可靠的答案。

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

代码语言:javascript
复制
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 ),并且希望能够,不管API版本如何,都能够迭代这些值。

任何方向都是感激的。

EN

回答 15

Stack Overflow用户

回答已采纳

发布于 2013-05-19 02:52:24

使用C++,遍历枚举的唯一方法是将它们存储在数组中并遍历相同的数组。主要的挑战是如何跟踪enum声明和数组声明中相同的顺序?

您可以通过enum和数组中对它们进行排序的方式使自动化。我觉得这是一种很好的方式:

代码语言:javascript
复制
// 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这个文件:

代码语言:javascript
复制
// Enum.h
typedef enum {
#define NAME_VALUE(NAME,VALUE) NAME = VALUE
#include"CAPI_SUBTYPE_E_list.h"
#undef NAME_VALUE
}CAPI_SUBTYPE_E;

并将相同的文件用于数组和其他宏定义:

代码语言:javascript
复制
// 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中迭代如下:

代码语言:javascript
复制
for(unsigned int i = 0, size = sizeof(CAPI_SUBTYPE_E_Array)/sizeof(CAPI_SUBTYPE_E_Array[0]);
    i < size; ++i)

或者在C++11中很简单:

代码语言:javascript
复制
for(auto i : CAPI_SUBTYPE_E_Array)
票数 17
EN

Stack Overflow用户

发布于 2013-05-16 20:43:49

与C++实践相比,它更复杂,更接近C,但是您可以使用X宏。这是非常丑陋的,你需要保持正确的顺序。在C++中,我相信我们不需要迭代枚举,更不需要为枚举分配值(表面上,枚举值在每个编译中都是随机的)。所以,把它当作一个笑话吧:)

代码语言:javascript
复制
#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
}
票数 12
EN

Stack Overflow用户

发布于 2013-05-18 21:19:32

我同意已经给出的声明,即如果不修改或复制enum的定义,这是不可能的。然而,在C++11 (甚至C++03?)您可以提供一个语法,您只需(字面上)将枚举数定义从enum复制并粘贴到宏中。只要每个枚举数都有一个显式定义(使用=),这就可以工作。

编辑:即使不是每个枚举数都有一个显式定义,也可以扩展它来工作,但是在这种情况下不应该需要这样做。

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

用法示例:

代码语言:javascript
复制
// 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 << ", ";
    }
}

宏产生(有效):

代码语言:javascript
复制
namespace particle_enum_detail
{
    // definition of a type and some constants

    constexpr ParticleEnum all_particles[] = {
        PROTON,
        ELECTRON,
        MUON
    };
}
using particle_enum_detail::all_particles;

宏定义

代码语言:javascript
复制
#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这样的语法。这对于任何类型的声明都是非常容易的,但是它为存储名称带来了问题:

代码语言:javascript
复制
#define MAKE_ENUM(ASSIGNMEN1, ASSIGNMENT2) \
    enum my_enum { ASSIGNMENT1, ASSIGNMENT2 }; \
    my_enum all[] = { ASSIGNMENT1, ASSIGNMENT2 };
MAKE_ENUM(proton = 11, electron = 22);

收益:

代码语言:javascript
复制
enum my_enum { proton = 11, electron = 22 };    // would be OK
my_enum all[] = { proton = 11, electron = 22 }; // cannot assign to enumerator

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

代码语言:javascript
复制
enum my_enum { proton = 11, electron = 22 };
// alternatively
constexpr int proton = 11, electron = 12;
// the `constexpr` here is equivalent to a `const`

这还不能解决我们的问题,它只是说明,如果我们不需要枚举器的自动增量特性,我们可以很容易地用一个常量列表替换枚举数。

现在,使用操作符重载的语法技巧:

代码语言:javascript
复制
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 )将数字(1122)转换为particle类型的临时值,并通过隐式定义的ctor将临时值复制/移动到目标对象(protonelectron)。

相反,行(2)中的=被解析为对(op)的运算符调用,它有效地返回调用它的对象的副本(*this)。constexpr允许在编译时使用这些变量,例如在模板声明中。由于对constexpr函数的限制,我们不能简单地在(op)函数中返回*this。此外,constexpr还暗示了const的所有限制。

通过提供隐式转换运算符,可以在ParticleEnum类型的第(2)行中创建数组

代码语言:javascript
复制
// in struct particle
constexpr operator ParticleEnum() { return m; }

// in namespace particle_enum_detail
ParticleEnum all_particles[] = { proton = 11, electron = 22 };
票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16596471

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档