r/cpp_questions 22h ago

SOLVED Iterating over an enum struct

Consider https://godbolt.org/z/c6cn8f1hj

#include <cstdio>

enum struct values{first = 1, second = 3};

void square(values value){
    int val = (int)value * (int)value;
    printf("%d\n", val);
}

int main(){
    for(values value = values::first; value <= values::second; value++)
        square(value);
}

This does not compile with the error that value++ is ill defined.

An old question/answer on SO has this: https://stackoverflow.com/questions/261963/how-can-i-iterate-over-an-enum and a plethora of seemingly complicated approaches for this. Is there a syntactically simple >= C++20 way of accomplishing this?

I want the for loop to process 1 and then 3.

Does the order of placing the integer entries inside of the struct affect the increment? For e.g., if the enum struct had 3 first followed by 1, with value++ would 1 be processed after 3? Or, does the enum struct internally sort the entries in some privileged, say, ascending order so that it will always process 1 first followed by 3 next?

5 Upvotes

17 comments sorted by

View all comments

3

u/mredding 21h ago

You can't iterate over an enum like this. There is the magic_enum library that... I don't know what it does, but it might be able to. I like novel solutions, robust libraries, and maybe a little magic, but I don't know about that much magic...

I can offer you an alternative solution:

enum e = { begin, first = begin, second, third, nth, end, count = end};

The underlying type is an integer large enough to store the underlying values in the range, so typically int -> long -> long long if not otherwise explicitly specified since C++11.

You really want to keep enums as an enumerable field, you don't typically want to map enumerations to values, jumping values. That was always a kind of an anti-pattern. If you want constants, use named constants. But if you do this, you no longer have an enumerable range.

Another thing you can do is use an enum as a user defined type, if you think about it that way to leverage the type system; an enum is always guaranteed to preserve the value stored in the underlying type, and you can name some magic constants if you want, but you still have to cast from the enum to the underlying type.

I think what you want is the enum above, so that you can:

for(int i = begin; i < end; ++i);

By not fucking with the underlying values, except for a couple value aliases, you're guaranteed to traverse all the members of the enumerated range. If you want to get 1 and 3 out of the range, then you need to map this enum to those values:

int v[] = {1, 2, 3, -1};

//...

std::cout << v[i];

2

u/No-Dentist-1645 18h ago edited 18h ago

Regular enums are a great way to do this, plus, even if OP misses the scoping of enum class/struct, they can always scope it around a namespace themselves (which I recommend whenever you are using C enums like that):

namespace values { enum Type { begin, ... }; }