4

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?

Niall
  • 30,036
  • 10
  • 99
  • 142
csbako
  • 181
  • 1
  • 7
  • Do you need the class inheritance? Cant you just define the operator outside the class? – thorsan Apr 22 '16 at 12:23
  • 2
    The problem will be determining the number of items in the enum, skipped values, etc. You can't really increment a C-style enum. – JvO Apr 22 '16 at 12:47

6 Answers6

4

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_casting so that it works on enum classes too.

Live on Coliru

In case you don't want to grant all enums 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 :

  • Declare your enums in that namespace as 101010 recommends, in which case the operator is found via ADL;
  • using namespace incr_enum; when you want to import and use that operator in a local scope.
Community
  • 1
  • 1
Quentin
  • 62,093
  • 7
  • 131
  • 191
  • 1
    I think this is crucial (I was just typing this whole thing up), limiting the scope of a template `operator++` is important. If the OP **really** wants/needs a generic increment of an enum, the OP will need to limit that as much as possible. For example, `std::real_ops`, introducing general operators can be useful, but if used heavy handedly can also cause chaos. – Niall Apr 22 '16 at 13:05
  • @Niall I was on the fence about namespace polluting, since this is limited to enums. But it doesn't hurt to mention it either. – Quentin Apr 22 '16 at 13:14
  • It looks great, but unfortunately it does not work in VS2015. I have this error: 1>g:\work\enumtest\consoleapplication1\main.cpp(21): error C2783: 'E &operator ++(E &)': could not deduce template argument for '' 1> g:\work\enumtest\consoleapplication1\main.cpp(15): note: see declaration of 'operator ++' 1>g:\work\enumtest\consoleapplication1\main.cpp(21): error C2675: unary '++': 'Enum' does not define this operator or a conversion to a type acceptable to the predefined operator – csbako Apr 23 '16 at 21:45
  • it works with this modification in vs2015: template < class E, class = std::enable_if_t < std::is_enum< E >::value > > – csbako Apr 25 '16 at 15:51
1

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 enums 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.

Live Demo

101010
  • 41,839
  • 11
  • 94
  • 168
  • 1
    This of course assumes the underlying values of the enum are sequential and without gaps. It also doesn't prevent you from incrementing past the end of the enum. You'd need to insert an END_OF_ENUM guard value as the last enumerated value and then manually check if your incremented enum value is >= to it. – Fibbs Apr 22 '16 at 13:04
  • @Fibbles yes, strings attached. – 101010 Apr 22 '16 at 13:08
0

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);

}
0

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.

Community
  • 1
  • 1
PaperBirdMaster
  • 12,806
  • 9
  • 48
  • 94
0
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;
}
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
-1
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
};
csbako
  • 181
  • 1
  • 7
  • 1
    This answer could use some explanation, in particular why the anonymous namespace is used. – Niall Apr 26 '16 at 15:09
  • 2
    Welcome to Stack Overflow! While this code may answer the question, providing additional context regarding why and/or how it answers the question would significantly improve its long-term value. Please [edit] your answer to add some explanation. – CodeMouse92 Apr 27 '16 at 22:16