迭代非增量枚举

时间:2013-05-16 20:05:49

标签: c++ enums iterator

在您提出问题之前,我已经lookedlooked了解了这一点,并且无法找到可靠的答案。

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

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版本如何,都希望能够迭代超过这些价值观。

任何方向都表示赞赏。

15 个答案:

答案 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)行中,=指定了复制初始化,这是通过将数字(1122)转换为临时类型{来完成的。 {1}}通过使用(ctor)并通过隐式定义的ctor将临时值复制/移动到目标对象(particleproton)。

相反,第(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。它绝不是一个完美的脚本,但它符合我项目的费用。我鼓励任何人改进它并在这里给出提示。