Implementation of operators for enum class
Every operator in C++ on enums can be written without casting to an underlying type, but the result would be ridiculously verbose.
As an example:
size_t index( Colors c ) {
switch(c) {
case Colors::Black: return 0;
case Colors::Blue: return 1;
case Colors::White: return 2;
}
}
Color indexd_color( size_t n ) {
switch(n%3) {
case 0: return Colors::Black;
case 1: return Colors::Blue;
case 2: return Colors::White;
}
}
Colors increment( Colors c, size_t n = 1 ) {
return indexed_color( index(c) + n );
}
Colors decrement( Colors c, size_t n = 1 ) {
return indexed_color( index(c)+3 - (n%3) );
}
Colors& operator++( Colors& c ) {
c = increment(c)
return c;
}
Colors operator++( Colors& c, bool ) {
Colors retval = c;
c = increment(c)
return retval;
}
and a smart compiler will be able to turn these into operations that are directly on the base integral type.
But casting to a base integral type in the interface of your enum class
is not a bad thing. And operators are part of the interface for your enum class
.
If you don't like that loop through size_t
and consider it a fake cast, you can just write:
Colors increment( Colors c ) {
switch(c) {
case Colors::Black: return Colors::Blue;
case Colors::Blue: return Colors::White;
case Colors::White: return Colors::Black;
}
}
and similarly for decrement, and implement increment-by-n
as loops of repeated increment
.
The no-cast solution is to use switch. However, you can generate a pseudo-switch using templates. The principle is to recursively process all values of the enum using a template list (or a parameter pack). So, here are 3 methods I found.
Test enum:
enum class Fruit
{
apple,
banana,
orange,
pineapple,
lemon
};
The vanilla switch (live here):
Fruit& operator++(Fruit& f)
{
switch(f)
{
case Fruit::apple: return f = Fruit::banana;
case Fruit::banana: return f = Fruit::orange;
case Fruit::orange: return f = Fruit::pineapple;
case Fruit::pineapple: return f = Fruit::lemon;
case Fruit::lemon: return f = Fruit::apple;
}
}
The C++03-ish method (live here):
template<typename E, E v>
struct EnumValue
{
static const E value = v;
};
template<typename h, typename t>
struct StaticList
{
typedef h head;
typedef t tail;
};
template<typename list, typename first>
struct CyclicHead
{
typedef typename list::head item;
};
template<typename first>
struct CyclicHead<void,first>
{
typedef first item;
};
template<typename E, typename list, typename first = typename list::head>
struct Advance
{
typedef typename list::head lh;
typedef typename list::tail lt;
typedef typename CyclicHead<lt, first>::item next;
static void advance(E& value)
{
if(value == lh::value)
value = next::value;
else
Advance<E, typename list::tail, first>::advance(value);
}
};
template<typename E, typename f>
struct Advance<E,void,f>
{
static void advance(E& value)
{
}
};
/// Scalable way, C++03-ish
typedef StaticList<EnumValue<Fruit,Fruit::apple>,
StaticList<EnumValue<Fruit,Fruit::banana>,
StaticList<EnumValue<Fruit,Fruit::orange>,
StaticList<EnumValue<Fruit,Fruit::pineapple>,
StaticList<EnumValue<Fruit,Fruit::lemon>,
void
> > > > > Fruit_values;
Fruit& operator++(Fruit& f)
{
Advance<Fruit, Fruit_values>::advance(f);
return f;
}
The C++11-ish method (live here):
template<typename E, E first, E head>
void advanceEnum(E& v)
{
if(v == head)
v = first;
}
template<typename E, E first, E head, E next, E... tail>
void advanceEnum(E& v)
{
if(v == head)
v = next;
else
advanceEnum<E,first,next,tail...>(v);
}
template<typename E, E first, E... values>
struct EnumValues
{
static void advance(E& v)
{
advanceEnum<E, first, first, values...>(v);
}
};
/// Scalable way, C++11-ish
typedef EnumValues<Fruit,
Fruit::apple,
Fruit::banana,
Fruit::orange,
Fruit::pineapple,
Fruit::lemon
> Fruit_values11;
Fruit& operator++(Fruit& f)
{
Fruit_values11::advance(f);
return f;
}
(C++11-ish old version)
You may be able to extend by adding some preprocessor to remove the need to repeat the list of values.
enum class Colors { Black, Blue, White };
Colors operator++(Colors& color)
{
color = (color == Colors::White) ? Colors::Black : Colors(int(color) + 1);
return color;
}
Check in C++ Shell