16

What is the preferred simple method for iterating through an enum with contiguous values in C++? I found previous SO questions on this subject which involved creating custom operator++ etc, but this seems like overkill. So far the best I have come up with is:

enum {
   FOO,
   BAR,
   BLECH,
   NUM_ENUMS
} MyEnum;

//for (MyEnum m = FOO; m < NUM_ENUMS; ++m)             // compile error
//    ...

//for (MyEnum m = FOO; m < NUM_ENUMS; m = m + 1)       // compile error
//    ...

for (MyEnum m = FOO; m < NUM_ENUMS; m = MyEnum(m + 1)) // OK ?
    ...

Is this reasonable from a coding style perspective and is it likely to generate warnings (g++ -Wall ... seems happy with this) ?

Paul R
  • 208,748
  • 37
  • 389
  • 560
  • 5
    Maybe a side question is why would you want to iterate over an enum and if you really do, is there a better way to represent named values to iterate? How about using std::map container – woosah Jun 20 '13 at 11:05
  • 2
    This is good as far as you are not defining custom values for the elements, leaving spaces between them (e.g.: FOO = 1, BAR = 5, BLECH = 7) – dunadar Jun 20 '13 at 11:13
  • 1
    Your third try should be ok (see [this answer](http://stackoverflow.com/a/261986/237483)) – Christian Ammer Jun 20 '13 at 11:15
  • @dunadar: good point - I'll edit the question to make it clear that this is only for *contiguous* enum values. – Paul R Jun 20 '13 at 11:32
  • @woosah: my current requirement is for a C++ test harness for a C module where the C API takes an enum parameter which must be tested for all possible values of the enum. If new values are added to the enum then the test harness should ideally not need updating to test these additional values. – Paul R Jun 20 '13 at 11:35
  • 1
    I've also been wondering about how to iterate enums: [Using enum in loops and value consistency](http://stackoverflow.com/questions/13971544/using-enum-in-loops-and-value-consistency) – PaperBirdMaster Jun 20 '13 at 13:02
  • 1
    There's a library [Better Enums](https://github.com/aantron/better-enums) that provides the features you're looking for. It also allows for interpreting enums as strings, as well as many other features. The developers claim it might get into the C++17 features set. – ZeroPhase Feb 04 '17 at 03:56

2 Answers2

14

It is indeed safe.

This would have been undefined: MyEnum(int(NUM_ENUMS) + 1) because the value to hold (4) would be greater than what the enum can represent ([0, 3]); since you ensure that m is strictly lower than NUM_ENUMS it is safe to use MyEnum(m + 1).

On the other hand, note that you would have issues with customized enums such as:

enum OtherEnum {
    Foo = -1,
    Bar = 2,
    Baz = 8,
    NUM_OTHERENUM
};

so it is not a generic practice.

I would advise a generated array to iterate over instead:

MyEnum const Values[] = { FOO, BAR, BLECH };

Note that it is easily generated from the definition of the enum, and also avoid polluting the interface with a nonsensical value (business-wise).

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • Thanks - I've updated the question to clarify that my requirement is is only for contiguous enum values. – Paul R Jun 20 '13 at 11:38
1

Like Matthieu said it is perfectly safe to do so and does not go against the C++ standard in any way.

As a side note overloading ++ operator over enum is not only an overkill, but has issues already stated by others (e.g. if enum is not linear in values)

Therefore instead of defining an operator ++, what you need is an iterator.

I adapted the following solution from here regarding enums by deft_code but slightly tailored to your example.

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 MyEnum
{
   FOO,
   BAR,
   BLECH,
   NUM_ENUMS
};

int main()
{
   for( auto e: Enum<MyEnum>() )
   {
      std::cout << ((int)e) << std::endl;
   }
}

This may still be an overkill for you, but its much cleaner. Another piece of code but much more elegant approach is here. It is possible in the future enum classes introduced in C++11 may have an iterator in the standard.

update: Since you updated us that your values are contiguous and the fact that it is required for a C API (?) then the above will not be suitable

Community
  • 1
  • 1
woosah
  • 843
  • 11
  • 22
  • Thanks - the C++11 stuff is interesting but my enum is defined as part of a C API which needs to be called from a C++ test harness, so I need to work with a plain old C enum (albeit in a C++ context). – Paul R Jun 20 '13 at 11:43
  • @woosah The link provided appears to be dead. – ZeroPhase Feb 04 '17 at 03:57