Reputation: 201
I need a general template class without macro magic that I can use like this:
template<typename E>
class enum_operators
{
E& operator++( E& orig )
{
orig = static_cast< E >( orig + 1 );
return orig;
}
};
enum colors : public enum_operators< colors >
{
white,
red,
green,
blue
};
enum corners : public enum_operators< corners >
{
topleft,
topright,
bottomleft,
bottomright
};
Is it possible with variadic template or something else? How can I do that?
Upvotes: 4
Views: 2112
Reputation: 275385
namespace operator_support {
template<class T> struct tag_t{using type=T; constexpr tag_t(){}};
template<class T> constexpr tag_t<T> tag = {};
template<class T>
std::false_type operator_support::supports_plus_plus( tag_t<T> );
template<class E>
decltype(supports_plus_plus( tag<E> )) supports_plus_plus_v = {};
}
In order to make your enum supports_plus_plus_t<E>
be true_type
, simply write std::true_type supports_plus_plus< tag_t<E> >();
in the same namespace as your enum E
. ADL does the rest.
namespace plus_plus {
template <class E,
std::enable_if_t<operator_support::supports_plus_plus_v<E>>* =nullptr
>
E &operator ++ (E &e) {
return e = static_cast<E>(
static_cast<std::underlying_type_t<E>>(e) + 1
);
}
}
in order to use ++
you must:
Type std::true_type supports_plus_plus< tag_t<E> >();
in the namespace of the enum E
.
using namespace ::plus_plus;
where you want to call ++
on an enum
.
This restricts the use of ++
to enums that support it and deals with operator lookup issues. (body of ++
from @csbako).
You can make a macro:
#define PLUS_PLUS_POWERS_ACTIVATE(...) \
std::true_type supports_plus_plus< tag_t<__VA_ARGS__> >(); \
using namespace ::plus_plus
which takes an enum name (I use va args as C++ enum names may have embedded ,
s between <>
s, which macros don't like).
enum whatever {
a,b,c
};
PLUS_PLUS_POWERS_ACTIVATE(whatever);
int main() {
whatever x = a;
std::cout << x;
++x;
std::cout << x;
++x;
std::cout << x;
}
Upvotes: 0
Reputation: 201
namespace enum_operator
{
template<typename E>
E& operator++(E& e)
{
e = static_cast< E >(static_cast< int >( e ) + 1);
return e;
}
};
namespace
{
enum colors
{
white,
red,
green,
blue
};
enum corners
{
topleft,
topright,
bottomleft,
bottomright
};
using namespace enum_operator
};
Upvotes: -1
Reputation: 13320
Don't use operator ++
! What are you supposed to do if you have an enum like this?:
enum class other_enum : int
{
low = -3000,
fabada_asturiana = 0xfabada,
answer_to_life_universe_everything = 0b101010,
high = -low
};
As you can see, the values are not reachable increasing by one the previous one and don't even have a pattern; use iterators instead. Based on this answer:
// Shortcut to the enum map.
template <typename ENUM>
using enum_map = std::map<ENUM, const std::string>;
// The enum map.
template <typename ENUM>
enum_map<ENUM> enum_values{};
// Empty function to end the recursion.
void initialize() {}
// Initialize the enum map.
template <typename ENUM, typename ... args>
void initialize(const ENUM value, const char *name, args ... tail)
{
enum_values<ENUM>.emplace(value, name);
initialize(tail ...);
}
// Obtain the begin iterator to the enum
template <class ENUM, class = std::enable_if_t<std::is_enum<ENUM>{}>>
auto begin(ENUM &)
{
return enum_values<ENUM>.begin();
}
// Obtain the end iterator to the enum
template <class ENUM, class = std::enable_if_t<std::is_enum<ENUM>{}>>
auto end(ENUM &)
{
return enum_values<ENUM>.end();
}
The code above allows the user to create a map with enum values and a description string, it can be used like this:
int main()
{
initialize
(
white, "White",
red, "Red",
green, "Green",
blue, "Blue",
topleft, "Top Left",
topright, "Top Right",
bottomleft, "Bottom Left",
bottomright, "Bottom Right",
other_enum::low, "Lowest value",
other_enum::fabada_asturiana, "Typical Spanish",
other_enum::answer_to_life_universe_everything, "42",
other_enum::high, "Higher value"
);
...
return 0;
}
But the initialize
call is mandatory in order to make the whole thing work; isn't needed to put all the enums in one call though.
With all the code above, we can iterate the enums this way (Live demo):
for (const auto &v : colors{})
std::cout << v.first << '\n'; // Print values
for (const auto &v : corners{})
std::cout << v.second << '\n'; // Print names
for (const auto &v : other_enum{})
std::cout << (int)v.first << " = " << v.second << '\n'; // Print values & names
With this approach you can avoid the inheritance and work with the types directly. If you need to preserve the enum members order you can change the enum_map
to a container which preserves order or if you don't need to associate the value to a string the underlying container can be changed to a vector or a list.
Upvotes: 0
Reputation: 63124
Building on 101010's and wowofbob's answers :
template <class E, class = std::enable_if_t<std::is_enum<E>{}>>
E &operator ++ (E &e) {
return e = static_cast<E>(
static_cast<std::underlying_type_t<E>>(e) + 1
);
}
I've SFINAE'd away the operator for everything that is not an enum, and added proper static_cast
ing so that it works on enum class
es too.
In case you don't want to grant all enum
s under the sun the ability to be incremented, you can nest this operator in a namespace (let's say ::incr_enum
), and then you can either :
using namespace incr_enum;
when you want to import and use that operator in a local scope.Upvotes: 5
Reputation: 42899
You can't inherit a class
from an enum
or enum class
. In my humble opinion the best you can do is define your template overloaded operator as a free function and put it and all of the enum
s that you want to work with it in namespace (e.g., fancy
) and let name lookup do the rest:
namespace fancy {
enum colors { white, red, green, blue };
enum corners { topleft, topright, bottomleft, bottomright };
template<typename E>
E& operator++(E &e) {
e = static_cast<E>(static_cast<int>(e) + 1);
return e;
}
} // end of namespace fancy
This way you'd restrict your operator for working only with the stuff you have in your namespace.
Upvotes: 1
Reputation:
You don't really need a classes here.
This works fine:
#include <assert.h>
enum colors
{
white,
red,
green,
blue
};
enum corners
{
topleft,
topright,
bottomleft,
bottomright
};
template<class Enum>
Enum next(Enum e) {
// To cast back to Enum from `int`
return static_cast<Enum>(e + 1);
}
int main() {
colors c = white;
colors d = next(c);
assert(c == white);
assert(d == red);
}
Upvotes: 0