88

I have a recurrent chunk of code where I loop over all the members of an enum class.

The for loop that I currently use looks very unwieldly compared to the new range-based for.

Is there any way to take advantage of new C++11 features to cut down on the verbosity for my current for loop?

Current Code that I would like to improve:

enum class COLOR
{
    Blue,
    Red,
    Green,
    Purple,
    First=Blue,
    Last=Purple
};

inline COLOR operator++( COLOR& x ) { return x = (COLOR)(((int)(x) + 1)); }

int main(int argc, char** argv)
{
  // any way to improve the next line with range-based for?
  for( COLOR c=COLOR::First; c!=COLOR::Last; ++c )
  {
    // do work
  }
  return 0;
}

In other words, it would be nice if I could do something like:

for( const auto& c : COLOR )
{
  // do work
}
Alex Bitek
  • 6,529
  • 5
  • 47
  • 77
kfmfe04
  • 14,936
  • 14
  • 74
  • 140
  • 21
    Interesting. Ada's had this feature since 1983. – Shark8 Dec 14 '11 at 01:05
  • 8
    `(COLOR)(((int)(x) + 1))` Instead of `int`, consider using `std::underlying_type::type`. – James McNellis Dec 14 '11 at 01:06
  • 8
    Is it expected that Purple is skipped? – kennytm Dec 14 '11 at 01:09
  • 1
    @JamesMcNellis +1 for a great tip - tried to `#include `, but having trouble compiling on gcc 4.6.1 - will investigate headers. – kfmfe04 Dec 14 '11 at 01:11
  • 4
    @kfmfe04 `std::underlying_type` is [not supported on GCC 4.6](http://stackoverflow.com/questions/7613386/c-type-traits-overview/7613919#7613919). It will be supported on 4.7. There's an approximate emulation here: http://stackoverflow.com/a/7932617/46642. – R. Martinho Fernandes Dec 14 '11 at 01:20
  • 5
    Gah! so fast to accept. I usually wait 24 hours just to give all timezones a chance to come up with a better answer. – deft_code Dec 14 '11 at 02:01
  • What we need is something like the "values()" method that Java has for its enums. This could return some sort of set or map container that could be iterated over. This container could be computed at compile-time using templates. – jbruni Feb 19 '14 at 23:48
  • If you like a boost solution, see http://boost.2283326.n4.nabble.com/Range-Iterator-iterate-over-an-enumeration-td4641957.html '[Range][Iterator] iterate over an enumeration? ' – gast128 Jan 24 '19 at 08:27

12 Answers12

63

I personally don't like overloading the ++ operator for enums. Often incrementing an enum value doesn't really make sense. All that is really wanted is a way to iterator over the enum.

Below is an generic Enum class that supports iteration. It's functional but incomplete. A real implementation would do well to restrict access to the constructor and add all the iterator traits.

#include <iostream>

template< typename T >
class Enum
{
public:
   class Iterator
   {
   public:
      Iterator( int value ) :
         m_value( value )
      { }

      T operator*( void ) const
      {
         return (T)m_value;
      }

      void operator++( void )
      {
         ++m_value;
      }

      bool operator!=( Iterator rhs )
      {
         return m_value != rhs.m_value;
      }

   private:
      int m_value;
   };

};

template< typename T >
typename Enum<T>::Iterator begin( Enum<T> )
{
   return typename Enum<T>::Iterator( (int)T::First );
}

template< typename T >
typename Enum<T>::Iterator end( Enum<T> )
{
   return typename Enum<T>::Iterator( ((int)T::Last) + 1 );
}

enum class Color
{
   Red,
   Green,
   Blue,
   First = Red,
   Last = Blue
};

int main()
{
   for( auto e: Enum<Color>() )
   {
      std::cout << ((int)e) << std::endl;
   }
}
deft_code
  • 57,255
  • 29
  • 141
  • 224
  • 1
    Agreed. This is much, much better than overloading operators on the enumeration type itself. – James McNellis Dec 14 '11 at 02:25
  • +1 Nice - I have modified my own Enum class to follow your format (not in OP). The type-safety is nice, but `T from_string( const string& T )` is a bit of a pain as in C++, we can't overload on the return enum value. The same problem exists whether or not I use a template-Enum, but with template-Enum, it's just a little more verbose. – kfmfe04 Dec 14 '11 at 12:26
  • 10
    [Working complete implementation](http://stacked-crooked.com/view?id=bcee5da83cd5c0738a17962bc00ee82d), also note that I changed the value of `Last` to be more consistent with normal iterator ranges. – Mooing Duck Feb 19 '13 at 22:02
  • this is a nice but incomplete workaround: `enum class Color { red, green, blue, white = 20, First = red, Last=white }` - with the wrapper-class this will not produce the correct output. It only works with dense numbers. – ABaumstumpf Apr 30 '23 at 08:41
45
enum class Color {
    blue,
    red,
    green = 5,
    purple
};
const std::array<Color,4> all_colors = {Color::blue, Color::red, Color::green, Color::purple};

Then:

for (Color c : all_colors) {
    //...
}

Many times I use it like this, where I want a 'none' value:

// Color of a piece on a chess board
enum class Color {
    white,
    black,
    none
};
const std::array<Color,3> colors = {Color::white, Color::black};

template <typename CONTAINER>
bool has_item (CONTAINER const & c, typename CONTAINER::const_reference v) {
    return std::find(c.begin(), c.end(), v) != c.end();
}

bool is_valid (Color c) {
    return has_item(colors, c) || c == Color::none;
}

bool do_it (Color c) {
    assert(has_item(colors, c)); // here I want a real color, not none
    // ...
}

bool stop_it (Color c) {
    assert(is_valid(c));         // but here I just want something valid
    // ...
}
rubenvb
  • 74,642
  • 33
  • 187
  • 332
user1594322
  • 2,008
  • 3
  • 19
  • 16
  • 11
    This is a great answer! The redundancy of having the items in two places is a pity, but it's a much cleaner solution than the other solutions; this has no casting, and as you mention the values don't have to start at zero or be contiguous. You can also easily specify subsets of values e.g. darkColors and lightColors. It relies a bit on enums having a fairly small number of values, but they usually do anyway. – Jim Oldfield Nov 28 '14 at 20:36
  • An addendum to your answer: with this solution you can get the count of values with colors.size(). If you need a compile time constant (for the size of another array, for example) you can use std::tuple_size(decltype(colors))::value, which is admittedly a bit long winded but totally safe. – Jim Oldfield Nov 28 '14 at 20:38
  • 3
    Why is the size of the array specified as `3`? Shouldn't it be `2`? And if so, doesn't this illustrate that violating the DRY principle always leads to hard-to-see bugs? – Felix Dombek Jan 02 '18 at 04:34
  • 2
    Really bad if someone adds a value. Than your array is not covering all values!! – jaques-sam Oct 04 '18 at 09:31
40

Iterating enumerations with the enumeration itself as an iterator is a poor idea, and I recommend using an actual iterator as in deft_code's answer. But if this is really what you want:

COLOR operator++(COLOR& x) {
    return x = (COLOR)(std::underlying_type<COLOR>::type(x) + 1); 
}

COLOR operator*(COLOR c) {
    return c;
}

COLOR begin(COLOR r) {
    return COLOR::First;
}

COLOR end(COLOR r) {
    COLOR l=COLOR::Last;
    return ++l;
}

int main() { 
    //note the parenthesis after COLOR to make an instance
    for(const auto& c : COLOR()) {
        //do work
    }
    return 0;
}

Working here: http://ideone.com/cyTGD8


On the iterator side of things, the easiest way is simply:
const COLOR COLORS[] = {COLOR::Blue, COLOR::Red, COLOR::Green, COLOR::Purple};
const COLOR (&COLORREF)[(int)COLOR::Last+1] = COLORS;

int main() { 
    for(const auto& c : COLORS) {
        //do work
    }
    return 0;
}

As seen here: http://coliru.stacked-crooked.com/a/5d356cc91556d6ef

(The separate defintinion and the reference of the array makes it a compiler error if the number of colors doesn't match the number of elements in the array. Excellent easy safety check.)

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • 4
    Needs an `operator*` to make `COLOR` an input iterator. – R. Martinho Fernandes Dec 14 '11 at 01:02
  • @R.MartinhoFernandes what should the signature of this `operator*` look like? – kfmfe04 Dec 14 '11 at 01:06
  • 3
    @kfmfe04 I added it to the answer. – R. Martinho Fernandes Dec 14 '11 at 01:07
  • @R.MartinhoFernandes: Thanks, I don't have access compiler with ranged based for loops. Though it was beginning to occur to me that I should use an actual iterator to iterate instead of the colors themselves. That's probably the better answer. Does ++c work with an enum `class`? Probably not... – Mooing Duck Dec 14 '11 at 01:13
  • @R.MartinhoFernandes works great - tyvm (to Mooing Duck, too)! – kfmfe04 Dec 14 '11 at 01:16
  • 2
    @MooingDuck ++ doesn't work on strong typed enums, but kfmfe04 has an implementation on his question. – R. Martinho Fernandes Dec 14 '11 at 01:19
  • @MooingDuck With an enum class, you need to define an `operator++` – kfmfe04 Dec 14 '11 at 01:21
  • Works fine on GCC 4.7.2, but no operator++, so compilation fails. How should this operator be defined? – FreelanceConsultant Feb 19 '13 at 19:05
  • @EdwardBird: I didn't put in my answer since the question already had `operator++`, and it was also in the linked code, but I'll see if I can fix up the question a hair – Mooing Duck Feb 19 '13 at 19:22
  • @EdwardBird The implementation is included in the question. But use `std::underlying_type` (to determine the type to cast to) as James mentions in the comments instead of casting to `int` – Praetorian Feb 19 '13 at 19:22
  • Okay thanks. I think there might be a better way of doing what I want to do regardless though. – FreelanceConsultant Feb 19 '13 at 19:28
  • Two problems. First, if I'm not mistaken `return First;` needs to be `return COLOR::First;` (and likewise for `Last`). Second, making `end()` return the last value means that the `for` loop *won't* iterate over that value; the body of the loop will execute only for the first 3 of the 4 values of type `COLOR`. Your [working example](http://ideone.com/b7pORw) uses an old-style `enum` rather than an `enum class`, and it defines `Last` as a distinct value beyond the highest valid value, which is why it works. – Keith Thompson Sep 18 '15 at 20:45
  • Overloading the operators are brilliant, making enum class iterate-able. Thanks for sharing! – HCSF Oct 28 '18 at 08:24
8

I'm sure that you can iterate over the members of a C++ initializer_list, so I reckon I've done this in the past:

enum class Color {Red, Green, Blue};

for (const Color c : {Color::Red, Color::Green, Color::Blue})
{
}

Whether there are issues with this, I don't know, but I thought I'd suggest it as it is concise, but not ideal if there are a lot of Colors.

Coder_Dan
  • 1,815
  • 3
  • 23
  • 31
  • This works but is not maintenance friendly. Consider when you are adding an new enum. We tag our enums with begin and end (e.g. eCol0rBegin = 0 and eColorEnd) and preferably you want the code to pick it up everywhere automatically. – gast128 Jan 24 '19 at 07:52
  • Yes @gast128 - you're right. If that was going to be a potential issue, then I would add an end marker to the enum like: enum class Color {Red, Green, Blue, Count}; I can then static_assert that Count has not changed. – Coder_Dan Jan 28 '19 at 11:41
7

You could probably do something clever with boost::mpl, a rough version might look like:

#include <typeinfo>

// ---------------------------------------------------------------------------|
// Boost MPL
// ---------------------------------------------------------------------------|
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/iterator_range.hpp>
#include <boost/mpl/range_c.hpp>

namespace mpl = boost::mpl;

using namespace std;

enum class COLOR 
{ 
   Blue,
   Red,
   Green,
   Purple,
   Last
};

struct enumValPrinter
{
    template< typename T >
    void operator() (const T&)
    {
        cout << "enumValPrinter with: " << typeid( T ).name() << " : " 
             << T::value << "\n";
    }
};

int main(int, char**)
{
    typedef mpl::range_c< int, static_cast<int>( COLOR::Blue ), 
                            static_cast<int>( COLOR::Last ) > Colors;
    mpl::for_each< Colors >( enumValPrinter() );
    return 0;
}
mark
  • 7,381
  • 5
  • 36
  • 61
4

If you're a terrible person you can get this behavior with the preprocessor, something like:

#include <vector>
#include <cstdio>

#define ENUM_NAME COLOR
#define ENUM_VALUES \
    ENUM_VALUE(Blue) \
    ENUM_VALUE(Red) \
    ENUM_VALUE(Green) \
    ENUM_VALUE(Purple)

// This block would be a #include "make_iterable_enum.h"
#define ENUM_VALUE(v) v,
enum class ENUM_NAME {ENUM_VALUES};
#undef ENUM_VALUE
#define ENUM_VALUE(v) ENUM_NAME::v,
#define VECTOR_NAME(v) values_ ## v
#define EXPAND_TO_VECTOR_NAME(v) VECTOR_NAME(v)
const std::vector<ENUM_NAME> EXPAND_TO_VECTOR_NAME(ENUM_NAME){ENUM_VALUES};
#undef ENUM_VALUE
#undef ENUM_NAME
#undef ENUM_VALUES
#undef VECTOR_NAME
#undef EXPAND_TO_VECTOR_NAME
// end #included block

int main() {
    for (auto v : COLOR_values) {
        printf("%d\n", (int)v);
    }
}

With minor modifications this could also support eg. ENUM_SETVALUE(Blue, 4) and making a const map from eg. COLOR::Blue to "Blue". And vice-versa.

I wish the standard had just built these features in as options to enum class. None of the workarounds are good.

4

Here's a tested example (GCC 4.6.1):

enum class COLOR
{
    Blue,
    Red,
    Green,
    Purple,
    First=Blue,
    Last=Purple
};

COLOR operator++( COLOR& x ) { return x = (COLOR)(((int)(x) + 1)); }

COLOR operator*(COLOR c) {return c;}

COLOR begin(COLOR r) {return COLOR::First;}
// end iterator needs to return one past the end!
COLOR end(COLOR r)   {return COLOR(int(COLOR::Last) + 1);}


int main()
{
    for (const auto& color : COLOR()) std::cout << int(color); //0123
    return 0;
}
jrok
  • 54,456
  • 9
  • 109
  • 141
  • 1
    This does also work with Intel C++ 2013 SP1 on Linux, when you install Update 1 this fails to compile, we reported an issue to Intel Engineering for that. – Johnny Willemsen Oct 28 '13 at 10:06
2

My two cents: as a complete unashamed hijacking of @matthiascy solution, and coming back to @deft_code philosophy, I introduced default values to _First and _Last template's arguments in order to be able to loop through part of the enum. Doing so, of course, we need again the First and Last in the enum class (which is why this is a hijacking).

template< typename T, T _First = T::First, T _Last= T::Last >
class Enum
{
public:
   class Iterator
   {
   public:
      Iterator( int value ) :
         m_value( value )
      { }

      T operator*( void ) const
      {
         return (T)m_value;
      }

      void operator++( void )
      {
         ++m_value;
      }

      bool operator!=( Iterator rhs )
      {
         return m_value != rhs.m_value;
      }

   private:
      int m_value;
   };

};

template< typename T, T _First = T::First, T _Last= T::Last >
typename Enum<T, _First, _Last >::Iterator begin( Enum<T, _First, _Last> )
{
   return typename Enum<T, _First, _Last>::Iterator( (int)_First );
}

template< typename T, T _First = T::First, T _Last= T::Last >
typename Enum<T, _First, _Last>::Iterator end( Enum<T, _First, _Last> )
{
   return typename Enum<T, _First, _Last>::Iterator( ((int)_Last) + 1 );
}

For example, to loop through all the pins of an Arduino board:

  for( auto p: Enum<PIN>() ) {
    ...
  }

Or only through the pins of a bus:

  for( auto p: Enum<PIN, PIN::D0, PIN::D6>() ) {
    ...
  }
Jacques
  • 301
  • 2
  • 13
2

I like the idea a lot and have often wished for it.

The problem I see is what happens when there is a repeated numeric value for an enum item. All the implementations I see above require casts to integral type and ++. Ultimately, I think language support might be required to truly iterate over each item in all cases. It would remove the need to have First, Last or Begin, End although I don't object to this too much. It's like looking for begin() end() for containers.

enum class COLOR 
{
   Blue,
   Red,
   Green,
   Mauve = 0,
   Purple,
   Last
};

The numbering starts over at Mauve.

emsr
  • 15,539
  • 6
  • 49
  • 62
1

Whether or not you approve of incrementing enums there are times when it is useful. So here's a simple way of doing so:

enum class COLOR
{
    Blue,
    Red,
    Green,
    Purple,
    First=Blue,
    Last=Purple
};

COLOR c;

++( *reinterpret_cast<int*>( &c));

There is no overhead since the compiler will take care of the casting and de-referencing. Add range checking or other capabilities as necessary.

rm1948
  • 431
  • 2
  • 11
1

As a modification of @deft_code's answer, you don't need to define the First and the Last in your enum class, just add two parameters for the templated Enum class.

template< typename T, T _Fist, T _Last >
class Enum
{
public:
   class Iterator
   {
   public:
      Iterator( int value ) :
         m_value( value )
      { }

      T operator*( void ) const
      {
         return (T)m_value;
      }

      void operator++( void )
      {
         ++m_value;
      }

      bool operator!=( Iterator rhs )
      {
         return m_value != rhs.m_value;
      }

   private:
      int m_value;
   };

};

template< typename T, T _Fist, T _Last >
typename Enum<T, _First, _Last >::Iterator begin( Enum<T, _First, _Last> )
{
   return typename Enum<T, _First, _Last>::Iterator( (int)_First );
}

template< typename T, T _Fist, T _Last >
typename Enum<T, _First, _Last>::Iterator end( Enum<T, _First, _Last> )
{
   return typename Enum<T, _First, _Last>::Iterator( ((int)_Last) + 1 );
}
matthiascy
  • 75
  • 5
1

Extending, but also simplifying the previous answer from @rubenvb (wow, December 2016th already).

To easily iterate over the colors and have a means of providing a numerical or string value to each color (e.g. when you want the values in some Xml file).

enum class COLOR
{
    Blue,
    Red,
    Green,
    Purple,
};

std::map<COLOR,std::string> colors = {
 {COLOR::Blue,"Blue"},
 {COLOR::Red,"Red"},
 {COLOR::Green,"Green"},
 {COLOR::Purple,"Purple"}, // yay Whoopi, great movie
};

for (auto pair : colors) {
  do_something_with_color(pair.first);
  and_maybe_even_do_something_with_color_value(pair.second);
}

Maintenance is not even so hard, just make sure you got all your enums in the map.

ajabo
  • 111
  • 2