38 votes

Énumérer sur une énumération en C ++

En C ++, est-il possible d'énumérer sur une énumération (soit au moment de l'exécution ou de la compilation (préféré)) et d'appeler des fonctions / de générer du code pour chaque itération?

Exemple de cas d'utilisation:

 enum abc
{    
    start
    a,
    b,
    c,
    end
}    
for each (__enum__member__ in abc)
{    
    function_call(__enum__member__);    
}
 


Doublés plausibles:

55voto

Johannes Schaub - litb Points 256113

Pour ajouter à @StackedCrooked réponse, vous pouvez surcharger operator++, operator-- et operator* et ont itérateur comme fonctionnalité.

enum Color {
    Color_Begin,
    Color_Red = Color_Begin,
    Color_Orange,
    Color_Yellow,
    Color_Green,
    Color_Blue,
    Color_Indigo,
    Color_Violet,
    Color_End
};

namespace std {
template<>
struct iterator_traits<Color>  {
  typedef Color  value_type;
  typedef int    difference_type;
  typedef Color *pointer;
  typedef Color &reference;
  typedef std::bidirectional_iterator_tag
    iterator_category;
};
}

Color &operator++(Color &c) {
  assert(c != Color_End);
  c = static_cast<Color>(c + 1);
  return c;
}

Color operator++(Color &c, int) {
  assert(c != Color_End); 
  ++c;
  return static_cast<Color>(c - 1);
}

Color &operator--(Color &c) {
  assert(c != Color_Begin);
  return c = static_cast<Color>(c - 1);
}

Color operator--(Color &c, int) {
  assert(c != Color_Begin); 
  --c;
  return static_cast<Color>(c + 1);
}

Color operator*(Color c) {
  assert(c != Color_End);
  return c;
}

Nous allons tester avec quelques <algorithm> modèle

void print(Color c) {
  std::cout << c << std::endl;
}

int main() {
  std::for_each(Color_Begin, Color_End, &print);
}

Maintenant, Color est une constante itérateur bidirectionnel. Voici une classe réutilisable j'ai codé tout en le faisant manuellement ci-dessus. J'ai remarqué qu'elle pourrait fonctionner pour beaucoup plus d'énumérations, de sorte que de répéter le même code est assez fastidieux

// Code for testing enum_iterator
// --------------------------------

namespace color_test {
enum Color {
  Color_Begin,
  Color_Red = Color_Begin,
  Color_Orange,
  Color_Yellow,
  Color_Green,
  Color_Blue,
  Color_Indigo,
  Color_Violet,
  Color_End
};

Color begin(enum_identity<Color>) {
  return Color_Begin;
}

Color end(enum_identity<Color>) {
  return Color_End;
}
}

void print(color_test::Color c) {
  std::cout << c << std::endl;
}

int main() {
  enum_iterator<color_test::Color> b = color_test::Color_Begin, e;
  while(b != e)
    print(*b++);
}

La mise en œuvre de la façon suivante.

template<typename T>
struct enum_identity { 
  typedef T type; 
};

namespace details {
void begin();
void end();
}

template<typename Enum>
struct enum_iterator 
  : std::iterator<std::bidirectional_iterator_tag, 
                  Enum> {
  enum_iterator():c(end()) { }

  enum_iterator(Enum c):c(c) { 
    assert(c >= begin() && c <= end());
  }

  enum_iterator &operator=(Enum c) {
    assert(c >= begin() && c <= end());
    this->c = c; 
    return *this;
  }

  static Enum begin() {
    using details::begin; // re-enable ADL
    return begin(enum_identity<Enum>());
  }

  static Enum end() {
    using details::end; // re-enable ADL
    return end(enum_identity<Enum>());
  }

  enum_iterator &operator++() {
    assert(c != end() && "incrementing past end?");
    c = static_cast<Enum>(c + 1);
    return *this;
  }

  enum_iterator operator++(int) {
    assert(c != end() && "incrementing past end?");
    enum_iterator cpy(*this);
    ++*this;
    return cpy;
  }

  enum_iterator &operator--() {
    assert(c != begin() && "decrementing beyond begin?");
    c = static_cast<Enum>(c - 1);
    return *this;
  }

  enum_iterator operator--(int) {
    assert(c != begin() && "decrementing beyond begin?");
    enum_iterator cpy(*this);
    --*this;
    return cpy;
  }

  Enum operator*() {
    assert(c != end() && "cannot dereference end iterator");
    return c;
  }

  Enum get_enum() const {
    return c;
  }

private:
  Enum c;
};

template<typename Enum>
bool operator==(enum_iterator<Enum> e1, enum_iterator<Enum> e2) {
  return e1.get_enum() == e2.get_enum();
}

template<typename Enum>
bool operator!=(enum_iterator<Enum> e1, enum_iterator<Enum> e2) {
  return !(e1 == e2);
}

42voto

StackedCrooked Points 12247

C ++ n'a pas de fonctionnalité spécifique qui permet l'itération des valeurs enum. Malgré cela, le besoin s'en fait parfois sentir. Une solution de contournement courante consiste à ajouter des valeurs qui marquent le début et la fin. Par exemple:

 enum Color
{
    Color_Begin,
    Color_Red = Color_Begin,
    Color_Orange,
    Color_Yellow,
    Color_Green,
    Color_Blue,
    Color_Indigo,
    Color_Violet,
    Color_End
};

void foo(Color c)
{
}


void iterateColors()
{
    for (size_t colorIdx = Color_Begin; colorIdx != Color_End; ++colorIdx)
    {
        foo(static_cast<Color>(colorIdx));
    }
}
 

J'espère que cela t'aides.

4voto

Konrad Rudolph Points 231505

Ni est possible sans un peu de travail manuel. Une grande partie du travail peut être effectuée à l'aide de macros, si vous souhaitez explorer ce domaine.

2voto

Steve Jessop Points 166970

L'expansion sur ce que Konrad dit, il est possible de l'idiome dans le cas de "générer du code pour chaque itération" est d'utiliser un fichier inclus pour représenter l'énumération:

mes trucs.h:

#ifndef LAST_ENUM_ELEMENT
#define LAST_ENUM_ELEMENT(ARG) ENUM_ELEMENT(ARG)
#endif

ENUM_ELEMENT(foo)
ENUM_ELEMENT(bar)
LAST_ENUM_ELEMENT(baz)

// not essential, but most likely every "caller" should do it anyway...
#undef LAST_ENUM_ELEMENT
#undef ENUM_ELEMENT

enum.h:

// include guard goes here (but mystuff.h doesn't have one)

enum element {
    #define ENUM_ELEMENT(ARG) ARG,
    #define LAST_ENUM_ELEMENT(ARG) ARG
    #include "mystuff.h"
}

main.cpp:

#include "enum.h"
#define ENUM_ELEMENT(ARG) void do_##ARG();
#include "mystuff.h"

element value = getValue();
switch(value) {
    #define ENUM_ELEMENT(ARG) case ARG: do_##ARG(); break;
    #include "mystuff.h"
    default: std::terminate();
}

Donc, pour ajouter un nouvel élément "qux", vous l'ajouter à mes trucs.h et écrire l' do_qux fonction. Vous ne devez pas toucher à l'envoi du code.

Bien sûr, si les valeurs de votre enum doivent être de règle spécifique de non-nombres entiers consécutifs, puis vous vous retrouvez le maintien de l'enum définition et l' ENUM_ELEMENT(foo)... liste séparément, ce qui est pénible.

1voto

DigitalRoss Points 80400

Non

Cependant, vous pouvez définir votre propre classe qui implémente des fonctionnalités de type énumération avec des itérations. Vous vous rappelez peut-être une astuce des jours Java antérieurs à la 1.5, appelée "modèle de conception de type enum sécurisé". Vous pouvez faire l'équivalent C ++.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X