Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enumerate over an enum in C++

In C++, Is it possible to enumerate over an enum (either runtime or compile time (preferred)) and call functions/generate code for each iteration?

Sample use case:

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

Plausible duplicates:

  • C++: Iterate through an enum
  • Enum in C++ like Enum in Ada?
like image 779
jameszhao00 Avatar asked Sep 07 '09 19:09

jameszhao00


People also ask

Can you iterate over an enum in C?

you can iterate the elements like: for(int i=Bar; i<=Last; i++) { ... } Note that this exposes the really-just-an-int nature of a C enum. In particular, you can see that a C enum doesn't really provide type safety, as you can use an int in place of an enum value and vice versa.

Can you iterate through enum?

Enums don't have methods for iteration, like forEach() or iterator(). Instead, we can use the array of the Enum values returned by the values() method.

What is enumeration in C?

Enumeration or Enum in C is a special kind of data type defined by the user. It consists of constant integrals or integers that are given names by a user. The use of enum in C to name the integer values makes the entire program easy to learn, understand, and maintain by the same or even different programmer.


2 Answers

To add to @StackedCrooked answer, you can overload operator++, operator-- and operator* and have iterator like functionality.

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

Let's test with some <algorithm> template

void print(Color c) {   std::cout << c << std::endl; }  int main() {   std::for_each(Color_Begin, Color_End, &print); } 

Now, Color is a constant bidirectional iterator. Here is a reusable class i coded while doing it manually above. I noticed it could work for many more enums, so repeating the same code all over again is quite tedious

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

Implementation follows.

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); } 
like image 85
Johannes Schaub - litb Avatar answered Sep 20 '22 12:09

Johannes Schaub - litb


C++ currently does not provide enumerator iteration. Despite that, the need sometimes arises for this. A common workaround is to add values that mark the beginning and the ending. For example:

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));     } } 
like image 36
StackedCrooked Avatar answered Sep 21 '22 12:09

StackedCrooked