【问题标题】:Iterating over non-incremental Enum迭代非增量枚举
【发布时间】:2013-05-11 21:06:50
【问题描述】:

在你问之前,我已经在 SO 上为此 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 版本如何,我都希望能够进行迭代超过这些值。

感谢任何方向。

【问题讨论】:

  • C++ 不为迭代枚举提供任何直接支持。把它们塞进std::vectorstd::array
  • 我讨厌图书馆使用这样的枚举。矢量建议是可靠的。
  • 除了我之前的评论(感谢iammilind 激励我朝另一个方向发展)我建议使用initializer_list 而不是vectorarray,因为它是guaranteed to be a literal

标签: c++ enums iterator


【解决方案1】:

将它们放入数组或其他容器中并对其进行迭代。如果修改枚举,则必须更新将它们放入容器的代码。

【讨论】:

    【解决方案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

    【讨论】:

      【解决方案3】:

      它是关于比 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
      }
      

      【讨论】:

      • 我很好奇你所说的“表面上枚举值在每次编译中都是随机的”是什么意思,因为标准是默认值分配给从零值开始的枚举,除非指定了一个值。见cprogramming.com/tutorial/enum.htmllearncpp.com/cpp-tutorial/45-enumerated-types
      • @RichardChambers 是的,你说得对。我只是不喜欢将枚举用作索引(例如,用于遍历数组)。默认值促使我们这样做。我的意思是像这样的代码for(int i = 0; ENUM_FIRST; i &lt; ENUM_LAST; ++i) do_smth(data[i])。我也不喜欢程序员分配枚举值,因为标准并不能阻止我们重复值。因此,“值是随机的”实际上是指“尽管enum 的默认值是由标准保证的,但我更愿意认为枚举值是随机的”。
      • @fasked 当您需要解析以您无法控制的标准协议发送的字节流时,实际上需要手动定义值的枚举。但无论如何 +1。
      【解决方案4】:

      我同意已经给出的声明,即如果不更改或复制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) 行中,= 指定复制初始化,这是通过使用 ( ctor)并通过隐式定义的 ctor 将临时对象复制/移动到目标对象(protonelectron)。

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

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

      // in struct particle
      constexpr operator ParticleEnum() { return m; }
      
      // in namespace particle_enum_detail
      ParticleEnum all_particles[] = { proton = 11, electron = 22 };
      

      【讨论】:

      • 实际上我之前的回答是在讨论 C++11,但后来我意识到它并没有比当前的 C++03 设施有任何好处。目前我已更改为使用宏技巧来跟踪枚举/数组。似乎我们的两个答案都试图达到同样的效果。
      • @iammilind 我同意,我也读过你以前的版本。虽然我认为 C++11 没有必要在我的回答中使用 operator= 技巧(不过,可变参数宏不在 C++03 中)。 operator=constexpr,因为我以前使用非类型模板参数而不是容器类型 (std::initializer_list) 来收集枚举数,但是在使用不同的编译器时遇到了问题(在非类型模板参数的表达式中明确禁止赋值)。
      【解决方案5】:

      在 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 不错的解决方案,唯一的缺点是每个枚举需要一个头文件。
      • 我没有得到答案,如何在没有编译错误的情况下定义头文件,例如 CAPI_SUBTYPE_DD_ALL 将是未定义的,如何在头文件中接受以逗号结尾?我们不会得到编译器错误吗?标识符是未定义的错误消息
      • @dochoex,将头文件视为#define 宏替换。无论“file.h”中的内容是什么——都只是在找到#include&lt;file.h&gt; 的地方替换。例如在第一种情况下,CAPI_SUBTYPE_DD_ALL 将替换为 CAPI_SUBTYPE_DD_ALL = 13 包含在 enum CAPI_SUBTYPE_E 中。
      【解决方案6】:

      这是另一种方法。一个好处是,如果您在 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;
      }
      

      【讨论】:

      • "一个好处是,如果您在开关中省略枚举值,编译器可能会警告您:" -- 为此,您可能应该省略 default: 大小写,尤其是当它这样做的时候什么都不做。
      • 将枚举成员列表放入这样的开关中将允许编译器检测两个或多个枚举成员是否具有相同的值,但是如果存在一个枚举成员,它不会发出警告没有案例陈述。编译器并不关心 case 语句的求值结果,只要它们求值为某种整数常量类型的值,并且每个值在整个 switch 主体中都是唯一的。 tutorialspoint.com/cplusplus/cpp_switch_statement.htm
      • @hvd true 没有必要,但是一些编译器/警告集仍然会发出警告。它最初作为文档留在示例中,但我现在将其删除。谢谢。
      • @RichardChambers 它是编译器,启用警告集,并且依赖于枚举声明。
      • 使用 gcc 时,-Wswitch 选项将“每当 switch 语句具有枚举类型的索引并且缺少该枚举的一个或多个命名代码的大小写时发出警告。(存在默认标签防止此警告。)枚举范围之外的案例标签也会在使用此选项时引发警告。” gcc.gnu.org/onlinedocs/gcc-3.4.4/gcc/Warning-Options.html 虽然看起来 -Wswitch-enum 更合适。
      【解决方案7】:

      由于枚举不允许迭代,您必须创建枚举及其值范围的替代表示。

      我将采用的方法是嵌入类中的简单表查找。问题在于,当 API 使用新条目修改其枚举时,您还需要更新此类的构造函数。

      我将使用的简单类将包含一个用于构建表的构造函数以及几个用于迭代表的方法。由于您还想知道添加项目时表大小是否存在问题,您可以使用assert () 宏,该宏将在调试模式下发出assert()。在下面的源代码示例中,我使用预处理器来测试是否调试编译以及是否包含断言,以提供基本一致性检查的机制。

      我从 P. J. Plauger 在他的《标准 C 库》一书中借用了一个想法,即使用简单的查找表进行 ANSI 字符操作,其中字符用于索引到表中。

      要使用此类,您可以执行以下操作,使用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()方法以查看是否结束已达到迭代次数。此外,在构建对象之后,可以检查m_bTableError 数据成员以查看构建过程中是否存在错误。

      该类的源示例如下。 您需要使用 API 的枚举值更改构造函数来更新它们。 不幸的是,无法自动检查更新枚举,但我们确实在调试编译以检查表是否足够大,以及放入表中的枚举值是否在表大小范围内。

      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);
      }
      

      如果您愿意,可以添加一个额外的方法来检索迭代的当前值。请注意,Current() 方法不是递增到下一个,而是使用迭代索引当前所在的任何位置并从当前位置开始搜索。因此,如果当前位置是一个有效值,它只会返回它,否则它将找到第一个有效值。或者,您可以只返回索引指向的当前表值,如果该值无效,则设置错误指示符。

      CAPI_SUBTYPE_E CapiEnum::Current (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;
      }
      

      【讨论】:

        【解决方案8】:

        根据问题开头给出的文章,我得出了一个基于您知道无效范围的假设的解决方案。

        我真的很想知道这是否是一个好的解决方案。

        首先,用类似的东西结束你的枚举: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;
        }
        

        我只提供了一个预增量运算符。一个后自增运算符可以在以后实现。

        【讨论】:

          【解决方案9】:

          答案是“不,您不能在 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 编译器,我们可以编写代码来生成带有特定 enum 元素的 CAPI_SUBTYPE_E_LIST,前提是这些元素实际上在使用 SFINAE 的 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&lt;CAPI_SUBTYPE_E&gt; 包含B iff B 被定义为CAPI_SUBTYPE_E 的一个元素。这使您可以针对多个版本的库进行编译,并根据库中的内容在 enum 元素列表中获取一组不同的元素。您确实必须针对enums 元素的“最终”版本维护那个烦人的样板文件(通过宏或代码生成可能会更容易),但它应该在编译时自动处理以前的版本。

          遗憾的是,这需要大量维护工作。

          最后,您要求这是动态的:实现动态的唯一实用方法是将第 3 方 API 包装在知道 API 版本的代码中,并公开不同的 enum 值缓冲区(我会把它放在std::vector 中)取决于 API 的版本。然后,当您加载 API 时,您还加载了这个帮助程序包装器,然后它使用上述技术构建 enum 的元素集,并对其进行迭代。

          使用一些可怕的宏可以更容易地编写一些样板文件,例如通过使用__LINE__ 来索引递归类型来构建各种AddElementN 类型的SFINAE 代码。但这太可怕了。

          【讨论】:

            【解决方案10】:

            经过一些增强预处理后更清晰 (???)。

            你通过一个序列来定义你的枚举

            #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)
            

            【讨论】:

              【解决方案11】:

              我正在使用这种类型的结构来定义我自己的枚举:

              #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 字符串

              【讨论】:

                【解决方案12】:

                已经有很多关于这个问题的答案,但其中大多数要么非常复杂,要么效率低下,因为它们没有直接解决迭代有间隙的枚举的要求。到目前为止,每个人都说这是不可能的,他们是正确的,因为没有语言功能可以让你这样做。这当然并不意味着你不能,而且正如我们迄今为止的所有答案所看到的那样,有很多不同的方法可以做到这一点。这是我的方式,基于您提供的枚举以及它的结构不会发生太大变化的假设。当然,这种方法可以根据需要进行调整。

                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 );
                    }
                }
                

                或者类似的东西。

                【讨论】:

                  【解决方案13】:

                  使用高阶宏

                  这是我们在项目中一直使用的技术。

                  概念:

                  这个想法是生成一个名为 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

                  【讨论】:

                    【解决方案14】:

                    我最终想出的唯一真正的“解决方案”是创建一个预运行脚本,该脚本读取包含枚举的 c/c++ 文件并生成一个包含所有列表的类文件枚举作为向量。这与 Visual Studio 支持T4 Templates 的方式非常相似。在 .Net 世界中,这是很常见的做法,但由于我无法在那种环境中工作,所以我不得不这样做。

                    我编写的脚本是用 Ruby 编写的,但你可以用任何语言编写。如果有人想要源脚本,我上传了here。这绝不是一个完美的脚本,但它适合我的项目。我鼓励任何人改进它并在这里提供提示。

                    【讨论】:

                      【解决方案15】:

                      不涉及宏且(几乎)没有运行时开销的解决方案的开始:

                      #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 }
                      

                      【讨论】:

                        猜你喜欢
                        • 2011-02-07
                        • 1970-01-01
                        • 2011-10-06
                        • 2012-06-08
                        • 2017-02-03
                        • 2018-06-02
                        • 1970-01-01
                        • 2017-05-12
                        • 2018-08-29
                        相关资源
                        最近更新 更多