Iterando sobre Enum não incremental

Antes de perguntar, procurei e procurei por isso em SO e não consegui encontrar uma resposta sólida.

Eu preciso ser capaz de iterar dinamicamente em um enum que tenha valores não incrementais, como um exemplo:

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 ; 

O motivo pelo qual eu gostaria de poder fazer isso é porque o enum é fornecido em uma API (que não posso alterar, obviamente) e preferiria ser capaz de, independentemente da versão da API, iterar esses valores .

Qualquer direção é apreciada.

Com C ++, a única maneira de iterar através de enums é armazená-los em uma matriz e iterar pelo mesmo. O principal desafio é como rastrear o mesmo pedido na declaração de enum e na declaração de matriz?
Você pode automatizar a maneira como os encomenda no enum e no array. Eu sinto que esta é uma maneira decente:

 // 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. */ 

Agora você #include este arquivo em sua declaração de enum e declaração de matriz nos dois locais com redefinição de macro:

 // Enum.h typedef enum { #define NAME_VALUE(NAME,VALUE) NAME = VALUE #include"CAPI_SUBTYPE_E_list.h" #undef NAME_VALUE }CAPI_SUBTYPE_E; 

E coloque o mesmo arquivo para matriz com outra definição de macro:

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

Agora iterar em C ++ 03 como:

 for(unsigned int i = 0, size = sizeof(CAPI_SUBTYPE_E_Array)/sizeof(CAPI_SUBTYPE_E_Array[0]); i < size; ++i) 

ou ainda simples em C ++ 11:

 for(auto i : CAPI_SUBTYPE_E_Array) 

Trata-se de práticas complicadas e mais C que C ++, mas você pode usar macros X. É muito feio e você precisa manter o TABLE na ordem correta. Em C ++, acredito que não precisamos iterar sobre enumerações e mais, não precisamos atribuir valores à enumeração (aparentemente, o valor da enumeração é random em cada compilation). Então pense nisso como uma piada 🙂

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

Concordo com as declarações já dadas de que isso não é possível sem alterar ou copiar as definições do enum . No entanto, em C ++ 11 (talvez até C ++ 03?) Você pode ir além de fornecer uma syntax em que tudo o que você precisa fazer (literalmente) é copiar e colar as definições de enumerador do enum em uma macro. Isso funciona desde que cada enumerador tenha uma definição explícita (usando = ).

Edit: Você pode expandir isso para funcionar mesmo se nem todo enumerador tiver uma definição explícita, mas isso não deve ser necessário neste caso.

Eu desenvolvi isso para alguns físicos, então o exemplo é sobre partículas.

Exemplo de uso:

 // required for this example #include  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 << ", "; } } 

A macro rende para (efetivamente):

 namespace particle_enum_detail { // definition of a type and some constants constexpr ParticleEnum all_particles[] = { PROTON, ELECTRON, MUON }; } using particle_enum_detail::all_particles; 

definição macro

 #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(p) } \ {} \ constexpr operator storage_type() \ { return m; } \ template < typename T > \ constexpr iterable_enum_ operator= (T p) \ { return { static_cast(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 

Nota: o tipo iterable_enum_ pode ser definido uma vez fora da macro.


explicação macro

A ideia é permitir uma syntax como proton = 11, electron = 12 dentro da invocação da macro. Isso funciona muito fácil para qualquer tipo de declaração, mas ainda causa problemas para armazenar os nomes:

 #define MAKE_ENUM(ASSIGNMEN1, ASSIGNMENT2) \ enum my_enum { ASSIGNMENT1, ASSIGNMENT2 }; \ my_enum all[] = { ASSIGNMENT1, ASSIGNMENT2 }; MAKE_ENUM(proton = 11, electron = 22); 

rende para:

 enum my_enum { proton = 11, electron = 22 }; // would be OK my_enum all[] = { proton = 11, electron = 22 }; // cannot assign to enumerator 

Como acontece com muitos truques sintáticos, a sobrecarga do operador fornece uma maneira de superar esse problema; mas o operador de atribuição deve ser uma function de membro - e enums não são classs. Então, por que não usar alguns objects constantes em vez de um enum?

 enum my_enum { proton = 11, electron = 22 }; // alternatively constexpr int proton = 11, electron = 12; // the `constexpr` here is equivalent to a `const` 

Isso ainda não resolve nosso problema, apenas demonstra que podemos facilmente replace enums por uma lista de constantes se não precisarmos do recurso de incremento automático de enumeradores .

Agora, o truque sintático com sobrecarga do operador:

 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(p) } {} private: ParticleEnum m; }; constexpr iterable_enum_ proton = 11, electron = 22; // (1) iterable_enum_ all_particles[] = { proton = 11, electron = 22 }; // (2) 

O truque é, na linha (1), o = designa uma boot de cópia, que é feita convertendo o número ( 11 , 22 ) para um temporário de tipo particle usando o (ctor) e copiando / movendo o temporário através de um implicitamente definido para o object de destino ( proton , electron ).

Em contraste, o = na linha (2) é resolvido para uma chamada de operador para (op), que efetivamente retorna uma cópia do object no qual ele foi chamado ( *this ). O material constexpr permite usar essas variables ​​em tempo de compilation, por exemplo, em uma declaração de modelo. Devido a restrições nas funções constexpr , não podemos simplesmente retornar *this na function (op). Adicionalmente, constexpr implica todas as restrições de const .

Ao fornecer um operador de conversão implícito, você pode criar o array na linha (2) do tipo ParticleEnum :

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

Com base nos artigos fornecidos no início da pergunta, deduzi uma solução que se baseia no pressuposto de que você conhece as faixas de inválidos.

Eu realmente quero saber se esta é uma boa solução.

Primeiro, termine com algo assim: CAPI_END = 60 . Isso ajudará a interações. Então meu código é:

 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(invalid[i][1] + 1); break; } else { next = static_cast(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; } 

Estou fornecendo apenas um operador de pré-incremento. Um operador pós-incremento é deixado para ser implementado posteriormente.

A resposta é “não, você não pode iterar sobre os elementos de um enum em C ++ 03 ou C ++ 11″.

Agora, você pode descrever o conjunto de valores de um enum de uma maneira que possa ser entendida em tempo de compilation.

 template struct TypedEnumList {}; typedef TypedEnumList< CAPI_SUBTYPE_E, CAPI_SUBTYPE_NULL, // etc // ... CAPI_SUBTYPE_DIAG_ASG > CAPI_SUBTYPE_E_LIST; 

que lhe dá um tipo CAPI_SUBTYPE_E_LIST que encapsula a lista de valores enum .

Podemos então preencher um array com estes facilmente:

  template std::array GetRuntimeArray( TypedEnumList ) { return { Es... }; } auto Capis = GetRuntimeArray( CAPI_SUBTYPE_E_LIST() ); 

se você realmente precisar. Mas este é apenas um caso especial do caso mais geral de poder gerar código para cada elemento de sua enum CAPI_SUBTYPE_E – construir diretamente um loop for não é necessário.

Curiosamente, com um compilador C ++ 11 compatível, poderíamos escrever código que geraria nosso CAPI_SUBTYPE_E_LIST com elementos enum particulares, se esses elementos estivessem realmente em CAPI_SUBTYPE_E usando SFINAE. Isso seria útil porque podemos pegar a versão mais recente da API que podemos suportar e fazer com que ela seja degradada automaticamente (em tempo de compilation) se a API que compilarmos for mais primitiva.

Para demonstrar a técnica, vou começar com um enum brinquedo

 enum Foo { A = 0, /* B = 1 */ }; 

Imagine que B=1 esteja descomentado na versão mais moderna da API, mas não está lá no mais primitivo.

 template struct AddElementN: AddElementN {}; template struct AddElementN<-1, EnumList, void> { typedef EnumList type; }; template struct AddElementN<0, TypedEnumList, typename std::enable_if< Enum::A == Enum::A >::type >: AddElement<-1, TypedEnumList> {}; template struct AddElementN<1, TypedEnumList, typename std::enable_if< Enum::B == Enum::B >::type >: AddElement<0, TypedEnumList> {}; // specialize this for your enum to call AddElementN: template struct BuildTypedList; template<> struct BuildTypedList: AddElementN<1, TypedEnumList> {}; template using TypedList = typename BuildTypedList::type; 

Agora, se eu escrevesse esse direito, TypedList contém B iff B é definido como um elemento de CAPI_SUBTYPE_E . Isso permite compilar em mais de uma versão da biblioteca e obter um conjunto diferente de elementos na sua lista de elementos enum , dependendo do que está na biblioteca. Você precisa manter esse clichê irritante (que provavelmente poderia ser facilitado com macros ou geração de código) em relação à versão “final” dos elementos enum , mas ele deve manipular automaticamente versões anteriores em tempo de compilation.

Isso, infelizmente, requer muita manutenção para o trabalho.

Finalmente, sua exigência de que isso seja dynamic: a única maneira prática de ser dynamic é envolver a API de terceiros no código que sabe qual é a versão da API e expõe um buffer diferente de valores de enum (eu colocaria em um std::vector ) dependendo de qual é a versão da API. Em seguida, quando você carrega a API, também carrega esse wrapper auxiliar, que usa as técnicas acima para criar o conjunto de elementos do enum qual você deseja iterar.

Alguns desses clichês podem ser mais fáceis de escrever com algumas macros horríveis, como aquelas que constroem o código SFINAE do tipo AddElementN usando __LINE__ para indexar os tipos recursivos. Mas isso seria horrível.

Você não pode iterar sobre enum arbitrário em C ++. Para iterar, os valores devem ser colocados em algum contêiner. Você pode automatizar a manutenção de tal contêiner usando ‘enum classs’ como descrito aqui: http://www.drdobbs.com/when-enum-just-isnt-enough-enumeration-c/184403955http://www.drdobbs.com/ quando-enum-apenas-isnt-suficiente-enumeração-c / 184403955

Use macros de ordem superior

Aqui está a técnica que estamos usando em nossos projetos.

Conceito:

A idéia é gerar uma macro chamada LISTING, que contém a definição de pares nome-valor e usa outra macro como argumento. No exemplo abaixo, defini duas dessas macros auxiliares. ‘GENERATE_ENUM’ para gerar o enum e ‘GENERATE_ARRAY’ para gerar um array iterável. Claro que isso pode ser estendido conforme necessário. Eu acho que esta solução lhe dá o maior retorno para o fanfarrão. Conceitualmente, é muito semelhante à solução da iammilind .

Exemplo:

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

Uso:

Agora no código você pode se referir ao enum assim:

 CAPI_SUBTYPES::Enum eVariable = CAPI_SUBTYPES::DIAG_STD; 

Você pode iterar sobre a enumeração da seguinte forma:

 for (unsigned int i=0; i 

Nota:

Se a memory me serve corretamente, enums C ++ 11 vivem em seus próprios namespaces (como em Java ou C #), portanto o uso acima não funcionaria. Você teria que se referir aos valores enum como este CAPI_SUBTYPES :: Enum :: FooBar .

Um pouco mais claro (???) com um pouco de aumento de pré-processamento.

Você define seus enums por uma sequência

 #define CAPI_SUBTYPE_E_Sequence \ (CAPI_SUBTYPE_NULL)(0) \ (CAPI_SUBTYPE_DIAG_DFD)(1) ... 

então você pode automatizar (através de macros) a declaração do enum,

 DECL_ENUM(CAPI_SUBTYPE_E) ; 

a tabela que indexa

 DECL_ENUM_TABLE(CAPI_SUBTYPE_E); 

o número de enums / tamanho da tabela

 ENUM_SIZE(CAPI_SUBTYPE_E) 

e access a ele:

 ITER_ENUM_i(i,CAPI_SUBTYPE_E) 

Aqui está o texto completo.

 #include  // 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  int main() { for (int i=0; i #include  #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) 

o início de uma solução que não envolve macros e (quase) nenhuma sobrecarga de tempo de execução:

 #include  #include  #include  #include  template using has_value = std::integral_constant; template 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; 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 static iterator find() { using iter = typename boost::mpl::find::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(); cout << "found : " << animal << endl; while (animal != animals::find()) { cout << animal << endl; ++animal; } // will trigger the static_assert auto xx = animals::find(); return 0; } 

saída:

 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 } 

Colocá-los em uma matriz ou outro contêiner e iterar sobre isso. Se você modificar o enum, terá que atualizar o código que os coloca no container.

Como um enum não permite iteração, você deve criar uma representação alternativa do enum e seu intervalo de valores.

A abordagem que eu tomaria seria uma simples consulta de tabela incorporada em uma class. O problema é que, como a API modifica seu enum com novas inputs, você também precisa atualizar o construtor para essa class.

A class simples que eu usaria seria composta de um construtor para construir a tabela, juntamente com alguns methods para iterar sobre a tabela. Como você também quer saber se há um problema com o tamanho da tabela ao adicionar itens, você pode usar uma macro assert () que emita um assert() no modo de debugging. No exemplo de origem abaixo, eu uso o pré-processador para testar a compilation de debugging ou não e se a afirmação foi incluída ou não, de modo a fornecer um mecanismo para verificação de consistência básica.

Eu peguei emprestada uma idéia que vi de PJ Plauger em seu livro Standard C Library de usar uma tabela de consulta simples para operações de caracteres ANSI nas quais o caractere é usado para indexar em uma tabela.

Para usar essa class, você faria algo como o seguinte, que usa um loop for para percorrer o conjunto de valores na tabela. Dentro do corpo do loop, você faria o que quisesse com os valores enum.

 CapiEnum myEnum; for (CAPI_SUBTYPE_E jj = myEnum.Begin(); !myEnum.End(); jj = myEnum.Next()) { // do stuff with the jj enum value } 

Como essa class enumera os valores, escolhi arbitrariamente retornar o valor de CAPI_SUBTYPE_NULL nos casos em que chegamos ao final da enumeração. Portanto, o valor de retorno no caso de um erro de consulta de tabela está no intervalo válido, mas não pode ser dependido. Portanto, uma verificação do método End() deve ser feita para ver se o final de uma iteração foi atingido. Além disso, depois de fazer a construção do object, é possível verificar o membro de dados m_bTableError para ver se houve um erro durante a construção.

O exemplo de origem da class é o seguinte. Você precisaria atualizar o construtor com os valores enum para a API conforme eles são alterados. Infelizmente, não há muito que possa ser feito para automatizar a verificação de um enum de atualização, mas temos testes em uma compilation de debugging para verificar se a tabela é grande o suficiente e se o valor do enum sendo colocado na tabela está dentro intervalo do tamanho da tabela.

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

E se você quiser, pode adicionar um método adicional para recuperar o valor atual da iteração. Observe que, em vez de incrementar para o próximo, o método Current () usa o índice de iteração no momento e inicia a pesquisa a partir da posição atual. Portanto, se a posição atual for um valor válido, ela simplesmente retornará, caso contrário, encontrará o primeiro valor válido. Alternativamente, você poderia fazer isso apenas retornar o valor da tabela atual apontado pelo índice e se o valor for inválido, defina um indicador de erro.

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

Aqui está mais uma abordagem. Um bônus é que seu compilador pode avisá-lo se você omitir um valor enum em um switch :

 template 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(pSubtype)); return; } std::cout << "Skipped: " << pSubtype << '\n'; } template 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(First)); idx <= static_cast(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(pSubtype) << '\n'; } }; t_apply apply; Apply(apply); return 0; } 

I’m using this type of constructions to define my own enums:

 #include  namespace enumeration { struct enumerator_base : boost::noncopyable { typedef boost::unordered_map kv_storage_t; typedef kv_storage_t::value_type kv_type; typedef std::set 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 struct enumerator; template 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\ : enum_singleton< enumerator >\ { \ 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::instance()) std::cout << enumeration::enumerator::instance().name(x) << "=" << x << std::endl; return 0; } 

Also you can replace storage_ type to boost::bimap to have bidirectional correspondance int <==> string

There are a lot of answers to this question already, but most of them are either very complicated or inefficient in that they don’t directly address the requirement of iterating over an enum with gaps. Everyone so far has said that this is not possible, and they are sort of correct in that there is no language feature to allow you to do this. That certainly does not mean you can’t, and as we can see by all the answers so far, there are many different ways to do it. Here is my way, based on the enum you have provided and the assumption that it’s structure won’t change much. Of course this method can be adapted as needed.

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

Ou algo nesse sentido.

The only real ‘solution’ I finally came up with to solve this problem is to create a pre-run script that reads the c/c++ file(s) containing the enums and generates a class file that has a list of all the enums as vectors. This is very much the same way Visual Studio supports T4 Templates . In the .Net world it’s pretty common practice but since I can’t work in that environment I was forced to do it this way.

The script I wrote is in Ruby, but you could do it in whatever language. If anyone wants the source script, I uploaded it here . It’s by no means a perfect script but it fit the bill for my project. I encourage anyone to improve on it and give tips here.