Allow for Range-Based For with enum classes?

C++For LoopC++11Enums

C++ Problem Overview


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
}

C++ Solutions


Solution 1 - C++

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

Solution 2 - C++

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

Solution 3 - C++

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

Solution 4 - C++

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

Solution 5 - C++

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.

Solution 6 - C++

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

Solution 7 - C++

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.

Solution 8 - C++

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.

Solution 9 - C++

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>() ) {
    ...
  }

Solution 10 - C++

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.

Solution 11 - C++

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

Solution 12 - C++

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionkfmfe04View Question on Stackoverflow
Solution 1 - C++deft_codeView Answer on Stackoverflow
Solution 2 - C++user1594322View Answer on Stackoverflow
Solution 3 - C++Mooing DuckView Answer on Stackoverflow
Solution 4 - C++markView Answer on Stackoverflow
Solution 5 - C++Coder_DanView Answer on Stackoverflow
Solution 6 - C++jrokView Answer on Stackoverflow
Solution 7 - C++Raven BlackView Answer on Stackoverflow
Solution 8 - C++emsrView Answer on Stackoverflow
Solution 9 - C++JacquesView Answer on Stackoverflow
Solution 10 - C++rm1948View Answer on Stackoverflow
Solution 11 - C++matthiascyView Answer on Stackoverflow
Solution 12 - C++ajaboView Answer on Stackoverflow