2

I was googling around how to iterate over enums, and I found a variety of suggestions, like How can I iterate over an enum? Although it is OK, that all those methods must lead back to iterating over integers, I find the suggested solutions more or less a kind of hacking. Is there any deeper reason why this operation is not better supported; or from the other side, which one is more portable (including one between C and C++) and more standard-proof?

Community
  • 1
  • 1
katang
  • 2,474
  • 5
  • 24
  • 48
  • 4
    Could it possibly be because the very purpose of `enum` is to be a textual integer constant? – Lundin Mar 02 '16 at 10:48
  • C++ just lacks reflection. *"Given an enum type, tell me how many enums are defined"* and *"What is the value of enum number n"* are questions that could be answered at compile time, yet there is no way to express the question in C++. Read existing standards proposals, make a good case why this feature needs to be added and get it into C++17. It just seems it was not important enough for anyone to go through that painful process, so it hasn't been added. – nwp Mar 02 '16 at 10:53
  • 1
    Possible duplicate of [How can I iterate over an enum?](http://stackoverflow.com/questions/261963/how-can-i-iterate-over-an-enum) – Garf365 Mar 02 '16 at 10:54
  • @Lundin: That may be so in practice, but doesn't the name "enumeration" suggest that items in an `enum` can be enumerated, or iterated over? – M Oehm Mar 02 '16 at 10:56
  • @MOehm - only if you believe in design by pun. "Enumeration" refers to the **definition** of the type, not its intended use. – Pete Becker Mar 02 '16 at 15:23
  • It *is* possible in standard C++, but not in a direct way, and takes a bunch of metaprogramming. See [this library](https://github.com/aantron/better-enums) (disclaimer: author). This [answer](http://stackoverflow.com/questions/28828957/enum-to-string-in-modern-c-and-future-c17/31362042#31362042) explains the technique and [this page](http://aantron.github.io/better-enums/demo/C++17ReflectionProposal.html) adapts it to emulate one of the C++17 [proposals](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4428.pdf) for adding direct support to the standard. – antron Mar 02 '16 at 15:58

2 Answers2

5

To iterate over a generic enum, portably between C and C++, simply use look-up tables:

typedef enum
{
  hello = 123,
  world = 456
} hello_t;

const hello_t TABLE [ENUM_ITEMS] =
{
  hello,
  world
};

for(size_t i=0; i<ENUM_ITEMS; i++)
{
  printf("%d", (int)TABLE[i]);
}

Unfortunately there is no way to programatically get the constant ENUM_ITEMS, unless you have a non-specific enum with no values assigned, like enum { hello, world, ENUM_ITEMS }. If some enumeration constants are explicitly assigned numbers, then you can only do something hack-ish like this:

typedef enum
{
  ENUM_START = __LINE__,
  hello = 123,
  world = 456,
  ENUM_ITEMS = __LINE__ - ENUM_START - 1
} hello_t;
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • You could use an X macro to define both enum and data consistently. You'd then get `ENUM_ITEMS` via the size of your data table. – M Oehm Mar 02 '16 at 10:59
  • @MOehm Yes, but I consider that an even worse solution than the above hack, because "X macros" always turn your code into an unreadable mess. – Lundin Mar 02 '16 at 11:00
  • Well, I'm just saying. Agreed, X macros are uglier than a "native" syntax, but they have their uses. – M Oehm Mar 02 '16 at 11:07
  • @ Lundin In my eyes, one of the the advantages of using enum-s is that they are typed. To my taste, the mentioned C++ solutions make the code at least less readable, and this C-style solution lacks typing. However, I like this trick with __LINE__. – katang Mar 02 '16 at 11:30
  • @katang C compatible means non-existent typing, at least as far as the compiler is concerned. Though good C compilers have an option to warn against implicit conversions between int and enum. – Lundin Mar 02 '16 at 12:02
  • Do you need the `__LINE__` hack at all? It just tells you how many enumerated items there are, but your `TABLE` could derive its length from the number of initialised items instead of having an explicit dimension. (I know you don't like X macros, but I find the use of `__LINE__` to count items a bit dangerous.) – M Oehm Mar 02 '16 at 12:10
  • @MOehm Ideally you could indeed declare the array as `const hello_t TABLE []` and then `_Static_assert(sizeof(TABLE)/sizeof(*TABLE) == ENUM_ITEMS, "bleh")`. That's how production quality code will look. However, you would still need the `ENUM_ITEMS` constant. Indeed the solution with `__LINE__` is questionable, someone could add blank lines. Hence the disclaimer "hack-ish". – Lundin Mar 02 '16 at 12:16
  • Why not `const hello_t TABLE [] = { hello, world }; #define ENUM_ITEMS (sizeof(TABLE)/sizeof(*TABLE))` to programatically define `ENUM_ITEMS`? – chux - Reinstate Monica Mar 02 '16 at 16:44
2

C or C++ ? It's not the same thing.

You can't iterate over enum in C, because enum is just switched by there number at the compilation time. however, if your sure of what's inside your enum, you can "iterate", like that :

enum color {
  YELLOW,
  GREEN,
  BLUE,
  RED,
  /* Please, add new color BEFORE this comment */
  NB_COLOR
};

for (int i = 0; i < NB_COLOR; ++i) {
   /* Do something */
}

But it's more like a hack as you say it, because you can't be sure that your enum start with 0 and you can't be sure that there is not "empty slot" between enum in C.

Tom's
  • 2,448
  • 10
  • 22
  • It's not necessarily a hack. If the design of your enum is such that you use only automatically enumerated values, putting a constant to define the number of possible values at the end is good practice, I think. – M Oehm Mar 02 '16 at 17:49
  • My bad. I say it's a hack because I have in mind example such like errno, where you can't rely on value in the enum (ERANGE, EPIPE, etc etc) because you don't write this enum. of course, for enum you write yourself, I think you can do what I write (but adding a comment like "Do not modify the value of this enumeration" and "add further value before this comment" is needed, because if someone else mess with your enum, the program is doomed). – Tom's Mar 03 '16 at 08:00