Changing enum to next value [C++11]
As noted by Maroš Beťko, to add 1 to a variable, you have to cast the value to int
and back:
activeDraw = static_cast<drawMode>(static_cast<int>(activeDraw) + 1);
If the enum is defined without the C++11 enum class
syntax (like in the question's text), the casting to int
is not necessary:
activeDraw = static_cast<drawMode>(activeDraw + 1);
To make it cycle back to zero, use integer arithmetic, modulo operator:
activeDraw = static_cast<drawMode>((activeDraw + 1) % (ATMOSPHERE + 1));
To eliminate one ugly +1
, add another element to the enum:
enum drawMode { ..., ATMOSPHERE, NUM_DRAW_MODES };
...
activeDraw = static_cast<drawMode>((activeDraw + 1) % NUM_DRAW_MODES);
You can also stuff this code into a operator++
if you use it very often:
drawMode operator++(drawMode& mode)
{
mode = static_cast<drawMode>((mode + 1) % NUM_DRAW_MODES);
return mode;
}
drawMode operator++(drawMode& mode, int) // postfix operator
{
drawMode result = mode;
++mode;
return result;
}
Overloading operators for enum
s is rarely used, and some people consider it overkill (bad), but it will make your code shorter (and arguably cleaner).
Since your enumerates don't have a forced value, you could "increase" them, and perform a modulo on the last item + 1 to reset to the first one when needed:
activeDraw = drawMode((activeDraw+1) % (ATMOSPHERE+1));
BTW: also works in C language with a slight modification:
activeDraw = (activeDraw+1) % (ATMOSPHERE+1);
This is something you should write once, use many places.
boost
has some operator libraries that might be useful. If you need to write your own, here is an example:
namespace EnumOps {
// ADL helper. See #define below for macro that writes
// the "this enum should use enum ops" overload:
template<class T>
std::false_type use_enum_ops_f(T&&){return {};}
// trait class that detects if we should be messing with this enum:
template<class T>
using use_enum_ops = decltype(use_enum_ops_f( std::declval<T>() ));
// to-from underlying type:
template<class E,
std::enable_if_t< use_enum_ops<E>{}, int> =0
>
constexpr std::underlying_type_t<E> get_underlying(E e) {
return static_cast<std::underlying_type_t<E>>(e);
}
template<class E,
std::enable_if_t< use_enum_ops<E>{}, int> =0
>
constexpr E from_underlying(std::underlying_type_t<E> e) {
return static_cast<E>(e);
}
// Clamps your Enum value from 0 to E::MAX_VALUE using modular arithmetic
// You must include a MAX_VALUE in your enum.
template<class E,
std::enable_if_t< use_enum_ops<E>{}, int> =0
>
E clamp_max( std::underlying_type_t<E> e ) {
constexpr auto max = get_underlying(E::MAX_VALUE);
if (e < 0) {
auto count = -(e-max+1)/max;
e = e + count*max;
}
return from_underlying<E>(e % max);
}
template<class E,
std::enable_if_t< use_enum_ops<E>{}, int> =0
>
E& operator+=( E& e, std::underlying_type_t<E> x ) {
e= clamp_max<E>(get_underlying(e) + x);
return e;
}
template<class E,
std::enable_if_t< use_enum_ops<E>{}, int> =0
>
E& operator-=( E& e, std::underlying_type_t<E> x ) {
e= clamp_max<E>(get_underlying(e) - x);
return e;
}
template<class E,
std::enable_if_t< use_enum_ops<E>{}, int> =0
>
E operator+( E e, std::underlying_type_t<E> x ) {
return e+=x;
}
template<class E,
std::enable_if_t< use_enum_ops<E>{}, int> =0
>
E operator+( std::underlying_type_t<E> x, E e ) {
return e+=x;
}
// no int - enum permitted, but enum-int is:
template<class E,
std::enable_if_t< use_enum_ops<E>{}, int> =0
>
E operator-( E e, std::underlying_type_t<E> x ) {
e -= x;
return e;
}
// enum-enum returns the distance between them:
template<class E,
std::enable_if_t< use_enum_ops<E>{}, int> =0
>
std::underlying_type_t<E> operator-( E lhs, E rhs ) {
return get_underlying(lhs) - get_underlying(rhs);
}
// ++ and -- support:
template<class E,
std::enable_if_t< use_enum_ops<E>{}, int> =0
>
E& operator++( E& lhs ) {
lhs += 1;
return lhs;
}
template<class E,
std::enable_if_t< use_enum_ops<E>{}, int> =0
>
E operator++( E& lhs, int ) {
auto tmp = lhs;
++lhs;
return tmp;
}
template<class E,
std::enable_if_t< use_enum_ops<E>{}, int> =0
>
E& operator--( E& lhs ) {
lhs -= 1;
return lhs;
}
template<class E,
std::enable_if_t< use_enum_ops<E>{}, int> =0
>
E operator--( E& lhs, int ) {
auto tmp = lhs;
--lhs;
return tmp;
}
}
// use this macro in the namespace of your enum
// passing it your enun name:
#define ENABLE_ENUM_OPS(...) \
std::true_type use_enum_ops_f(__VA_ARGS__){return {};}
// Where you wnat to use ops, you must also
// using namespace EnumOps;
Example use:
namespace somewhere {
enum class bob { A, B, C, MAX_VALUE };
ENABLE_ENUM_OPS(bob)
}
int main() {
using namespace EnumOps;
auto x = somewhere::bob::A;
++x;
std::cout << (x == somewhere::bob::B) << "\n";
x+=3;
std::cout << (x == somewhere::bob::B) << "\n";
x-=4;
std::cout << (x == somewhere::bob::A) << "\n";
}
live example.
This uses modest amount of C++14 -- the std::underlying_type_t<E>
. Replace with typename std::underlying_type<E>::type
. And similar for any other _t
aliases I sneaked in.
It uses C++11 features that MSVC 2015 fails at miserably. Use a C++11 compiler to fix that problem. It may appear to initially work in MSVC 2015, but do not be fooled. I have not tried it on MSVC 2017.