Enumerate over an enum in C++
Solution 1:
To add to @StackedCrooked answer, you can overload operator++
, operator--
and operator*
and have iterator like functionality.
enum Color {
Color_Begin,
Color_Red = Color_Begin,
Color_Orange,
Color_Yellow,
Color_Green,
Color_Blue,
Color_Indigo,
Color_Violet,
Color_End
};
namespace std {
template<>
struct iterator_traits<Color> {
typedef Color value_type;
typedef int difference_type;
typedef Color *pointer;
typedef Color &reference;
typedef std::bidirectional_iterator_tag
iterator_category;
};
}
Color &operator++(Color &c) {
assert(c != Color_End);
c = static_cast<Color>(c + 1);
return c;
}
Color operator++(Color &c, int) {
assert(c != Color_End);
++c;
return static_cast<Color>(c - 1);
}
Color &operator--(Color &c) {
assert(c != Color_Begin);
return c = static_cast<Color>(c - 1);
}
Color operator--(Color &c, int) {
assert(c != Color_Begin);
--c;
return static_cast<Color>(c + 1);
}
Color operator*(Color c) {
assert(c != Color_End);
return c;
}
Let's test with some <algorithm>
template
void print(Color c) {
std::cout << c << std::endl;
}
int main() {
std::for_each(Color_Begin, Color_End, &print);
}
Now, Color
is a constant bidirectional iterator. Here is a reusable class i coded while doing it manually above. I noticed it could work for many more enums, so repeating the same code all over again is quite tedious
// Code for testing enum_iterator
// --------------------------------
namespace color_test {
enum Color {
Color_Begin,
Color_Red = Color_Begin,
Color_Orange,
Color_Yellow,
Color_Green,
Color_Blue,
Color_Indigo,
Color_Violet,
Color_End
};
Color begin(enum_identity<Color>) {
return Color_Begin;
}
Color end(enum_identity<Color>) {
return Color_End;
}
}
void print(color_test::Color c) {
std::cout << c << std::endl;
}
int main() {
enum_iterator<color_test::Color> b = color_test::Color_Begin, e;
while(b != e)
print(*b++);
}
Implementation follows.
template<typename T>
struct enum_identity {
typedef T type;
};
namespace details {
void begin();
void end();
}
template<typename Enum>
struct enum_iterator
: std::iterator<std::bidirectional_iterator_tag,
Enum> {
enum_iterator():c(end()) { }
enum_iterator(Enum c):c(c) {
assert(c >= begin() && c <= end());
}
enum_iterator &operator=(Enum c) {
assert(c >= begin() && c <= end());
this->c = c;
return *this;
}
static Enum begin() {
using details::begin; // re-enable ADL
return begin(enum_identity<Enum>());
}
static Enum end() {
using details::end; // re-enable ADL
return end(enum_identity<Enum>());
}
enum_iterator &operator++() {
assert(c != end() && "incrementing past end?");
c = static_cast<Enum>(c + 1);
return *this;
}
enum_iterator operator++(int) {
assert(c != end() && "incrementing past end?");
enum_iterator cpy(*this);
++*this;
return cpy;
}
enum_iterator &operator--() {
assert(c != begin() && "decrementing beyond begin?");
c = static_cast<Enum>(c - 1);
return *this;
}
enum_iterator operator--(int) {
assert(c != begin() && "decrementing beyond begin?");
enum_iterator cpy(*this);
--*this;
return cpy;
}
Enum operator*() {
assert(c != end() && "cannot dereference end iterator");
return c;
}
Enum get_enum() const {
return c;
}
private:
Enum c;
};
template<typename Enum>
bool operator==(enum_iterator<Enum> e1, enum_iterator<Enum> e2) {
return e1.get_enum() == e2.get_enum();
}
template<typename Enum>
bool operator!=(enum_iterator<Enum> e1, enum_iterator<Enum> e2) {
return !(e1 == e2);
}
Solution 2:
C++ currently does not provide enumerator iteration. Despite that, the need sometimes arises for this. A common workaround is to add values that mark the beginning and the ending. For example:
enum Color
{
Color_Begin,
Color_Red = Color_Begin,
Color_Orange,
Color_Yellow,
Color_Green,
Color_Blue,
Color_Indigo,
Color_Violet,
Color_End
};
void foo(Color c)
{
}
void iterateColors()
{
for (size_t colorIdx = Color_Begin; colorIdx != Color_End; ++colorIdx)
{
foo(static_cast<Color>(colorIdx));
}
}
Solution 3:
Neither is possible without a little manual labour. A lot of the work can be done by macros, if you’re willing to delve into that area.
Solution 4:
Expanding on what Konrad says, one possible idiom in the case of "generate code for each iteration" is to use an included file to represent the enumeration:
mystuff.h:
#ifndef LAST_ENUM_ELEMENT
#define LAST_ENUM_ELEMENT(ARG) ENUM_ELEMENT(ARG)
#endif
ENUM_ELEMENT(foo)
ENUM_ELEMENT(bar)
LAST_ENUM_ELEMENT(baz)
// not essential, but most likely every "caller" should do it anyway...
#undef LAST_ENUM_ELEMENT
#undef ENUM_ELEMENT
enum.h:
// include guard goes here (but mystuff.h doesn't have one)
enum element {
#define ENUM_ELEMENT(ARG) ARG,
#define LAST_ENUM_ELEMENT(ARG) ARG
#include "mystuff.h"
}
main.cpp:
#include "enum.h"
#define ENUM_ELEMENT(ARG) void do_##ARG();
#include "mystuff.h"
element value = getValue();
switch(value) {
#define ENUM_ELEMENT(ARG) case ARG: do_##ARG(); break;
#include "mystuff.h"
default: std::terminate();
}
So, to add a new element "qux", you add it to mystuff.h and write the do_qux
function. You don't have to touch the dispatch code.
Of course if the values in your enum need to be specific non-consecutive integers, then you end up maintaining the enum definition and the ENUM_ELEMENT(foo)
... list separately, which is messy.
Solution 5:
No
However, you could define your own class that implements enum-like features with iterations. You may recall a trick from the pre 1.5 Java days, called the "type safe enum design pattern". You could do the C++ equivalent.