tags:

views:

832

answers:

6

Greetings, everyone!

I have a c++ code, where the following enum is declared:

enum Some 
{
   Some_Alpha = 0,
   Some_Beta,
   Some_Gamma,
   Some_Total
};
int array[Some_Total];

The values of Alpha, Beta and Gamma are sequential, and I gladly use the following cycle to iterate through them:

for ( int someNo = (int)Some_Alpha; someNo < (int)Some_Total; ++someNo ) {}

This cycle is ok, until I decide to change the order of the declarations in the enum, say, making Beta the first value and Alpha - the second one. That invalidates the cycle header, because now I have to iterate from Beta to Total. So, what are the best practices of iterating through enum? I want to iterate through all the values without changing the cycle headers every time. I can think of one solution:

enum Some 
{
   Some_Start = -1,
   Some_Alpha,
   ...
   Some_Total
};
int array[Some_Total];

and iterate from (Start + 1) to Total, but it seems ugly and I have never seen someone doing it in the code. Is there any well-known paradigm for iterating through the enum, or I just have to fix the order of the enum values? (let's pretend, I really have some awesome reasons for changing the order of the enum values)...

A: 
enum Some
{
   __Some_first__ = 0,
   Some_Alpha = __Some_first__,
....
   __Some_last__
};

Doing such you can grant first & last never changes order

Dewfy
Why the double underscores? Those names are illegal in C++.
anon
What do you mean illegal? It is just used to make an accent of them special purposes. And SadSido asked about paradigm, and nobody forces your to violate code convention.
Dewfy
Names that contain the double underscore are reserved by the C++ Standard for the C++ implementation - you are not allowed to use them in your own code.
anon
@Neil - you are right.
Dewfy
That is what I was looking for... Now iterating from Some_First to Some_Total does not depend on the order of other values. Thanks!
SadSido
Note: this still depends on the assumption that your enum values are continuous (i.e. only the start value is set).
Zed
@Dewfy if he's right why don't you fix your code??
Johannes Schaub - litb
@litb - I have already written about it: "sked about paradigm, and nobody forces your to violate code convention." This code is compiled at least at 3 compilers without any warning. I don't see error, but agree that exist world-wide convention about names.
Dewfy
@Dewfy: You don't see a _compiler error_. That's the way it is with some errors. The compiler doesn't find all of them. (You do obey the ODR, even though your compiler doesn't always find out, don't you?)
sbi
A: 

If you do not use any assignments, the enums are guaranteed to be sequential starting with 0 as the first. thers.

The best thing you can do is keep them in the order you want in your enum definition, and cycle through them with the for loop.

Sean A.O. Harney
+5  A: 

No, there is no way of doing this because there is no guarantee that someone hasn't written code like:

enum Some 
{
   Some_Alpha = 0,
   Some_Beta,
   Some_Gamma = 42,
   Some_Delta, 
  Some_Total
};
anon
As sbi has pointed out, there actually is a way of doing this that can handle such a case just fine.
Dan Moulding
+4  A: 

You can check out this article with its source code on how you can implement this with static class members.

Zed
+1, enums are too primitive a datatype to iterate over them, dr. dobb comes to the rescue.
Ozan
Great article, thanks! I remember the discussion of "typesafe enum" pattern in some Java book and this is just the same for c++
SadSido
A: 

I place all Enums in their own namespace. Example:

namespace Foo {
enum Enum {
    First=0, // must be sequential
    Second,
    Third,
    End // must be last
};
}

In code:

for (int i=Foo::First; i!=Foo::End; i++) {
// do stuff
}

This is because C++ allows stuff like this (not tested in a compiler):

enum Foo {
Alpha = 1
};

enum Bar {
Beta = 2
};

Foo foo = Beta;

Which is clearly wrong.

graham.reeds
Almost my enums are declared inside classes, which provide the scope. In C++, the class is the basic scoping unit, not the namespace.
anon
A class that exists just for holding an enum doesn't feel right.
graham.reeds
Well, neither does a namespace, as far as I'm concerned.
anon
Namespaces are great (and my original enum is hidden inside a class). Ok, but the question is that you are forced to change your enum to { Second, First, Third, End } and you must find and replace all the code, where you iterate through it.
SadSido
Normally you wouldn't change the order - if you do, things that rely on that order (if you are using enums at both ends of a comms system, writing the values to a file/database) will break.
graham.reeds
@graham: `Foo foo = Beta;` is indeed wrong -- which is why it isn't allowed in C++.
sbi
Like I said - I haven't tested the code in an actual compiler, but there are problem like enum DaysOfWeek { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }; DaysOfWeek days = Wednesday + Friday;
graham.reeds
@graham: This, too, doesn't compile in C++. I believe both of your examples compile in C. Are you perhaps confusing C and C++ here? (I wouldn't complain, but you specifically wrote _"because C++ allows stuff like this"_. And I do remember Stroustrup writing somewhere that he forbid that when he invented C++, because it is indeed unsafe and error-prone).
sbi
Well I am writing off the top of my head - I don't have access to a C++ compiler at my current work, and I am too lazy at home to dig one up and try it out, but I am pretty sure you can add with enums - they are only ints at heart.
graham.reeds
@graham: As I said, they are _in C_. They are _not_ in C++. Why don't you check this at http://www.comeaucomputing.com/tryitout? It's free and it's considered to be the most std-conforming compiler around.
sbi
+6  A: 

You can define an operator++() for your enum. This has the advantage that it uses the well-known paradigm of the standard incrementation operators. :)

Depending on whether your enums are contiguous, you can treat them as int or use a switch:

Some& operator++(Some& obj)
{
# if YOUR_ENUMS_ARE_CONTIGUOUS
  int i = obj;
  if( ++i > Some_Total ) i = Some_Alpha;
  return obj = static_cast<Some>(i);
# else 
  switch(obj)
  {
    case Some_Alpha : obj = Some_Beta;  break;
    case Some_Beta  : obj = Some_Gamma; break;
    case Some_Gamma : obj = Some_Total; break;
    case Some_Total : obj = Some_Alpha; break;
    default: assert(false); // changed enum but forgot to change operator
  }
  return obj;
# endif
}

Note that, if operator++() is defined, users will probably expect an operator--(), too.

sbi