Last active
November 28, 2016 10:48
-
-
Save elbeno/992801884dd13a8ce4bb to your computer and use it in GitHub Desktop.
Treating enum class as flags or iterable
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cstdint> | |
#include <iostream> | |
#include <iterator> | |
#include <type_traits> | |
using namespace std; | |
// ----------------------------------------------------------------------------- | |
// Traits for treating enums like flags, and/or giving the ability to loop over | |
// them | |
template <typename E, | |
typename = enable_if_t<is_enum<E>::value>> | |
struct enum_has_flag_behavior; | |
template <typename E, | |
typename = enable_if_t<is_enum<E>::value>> | |
struct enum_has_range_behavior; | |
// ----------------------------------------------------------------------------- | |
// Operations for treating enums like flags | |
template <typename E, | |
typename = enable_if_t<enum_has_flag_behavior<E>::value>> | |
constexpr inline E& operator|=(E& a, E b) | |
{ | |
using UT = underlying_type_t<E>; | |
return a = static_cast<E>(static_cast<UT>(a) | static_cast<UT>(b)); | |
} | |
template <typename E, | |
typename = enable_if_t<enum_has_flag_behavior<E>::value>> | |
constexpr inline E operator|(E a, E b) | |
{ | |
return a |= b; | |
} | |
template <typename E, | |
typename = enable_if_t<enum_has_flag_behavior<E>::value>> | |
constexpr inline E& operator&=(E& a, E b) | |
{ | |
using UT = underlying_type_t<E>; | |
return a = static_cast<E>(static_cast<UT>(a) & static_cast<UT>(b)); | |
} | |
template <typename E, | |
typename = enable_if_t<enum_has_flag_behavior<E>::value>> | |
constexpr inline E operator&(E a, E b) | |
{ | |
return a &= b; | |
} | |
template <typename E, | |
typename = enable_if_t<enum_has_flag_behavior<E>::value>> | |
constexpr inline E& operator^=(E& a, E b) | |
{ | |
using UT = underlying_type_t<E>; | |
return a = static_cast<E>(static_cast<UT>(a) ^ static_cast<UT>(b)); | |
} | |
template <typename E, | |
typename = enable_if_t<enum_has_flag_behavior<E>::value>> | |
constexpr inline E operator^(E a, E b) | |
{ | |
return a ^= b; | |
} | |
template <typename E, | |
typename = enable_if_t<enum_has_flag_behavior<E>::value>> | |
constexpr inline bool operator!(E a) | |
{ | |
return a == E{}; | |
} | |
template <typename E, | |
typename = enable_if_t<enum_has_flag_behavior<E>::value>> | |
constexpr inline E operator~(E a) | |
{ | |
using UT = underlying_type_t<E>; | |
return static_cast<E>(~static_cast<UT>(a)); | |
} | |
// ----------------------------------------------------------------------------- | |
// Operations for treating enums as ranges | |
template <typename E, | |
typename = enable_if_t<enum_has_range_behavior<E>::value>> | |
constexpr E operator++(E& a) | |
{ | |
using UT = underlying_type_t<E>; | |
return a = static_cast<E>(static_cast<UT>(a) + 1); | |
} | |
template <typename E, | |
typename = enable_if_t<enum_has_range_behavior<E>::value>> | |
constexpr E operator*(E a) | |
{ | |
return a; | |
} | |
template <typename E, | |
typename = enable_if_t<enum_has_range_behavior<E>::value>> | |
constexpr E begin(E) | |
{ | |
return enum_has_range_behavior<E>::begin; | |
} | |
template <typename E, | |
typename = enable_if_t<enum_has_range_behavior<E>::value>> | |
constexpr E end(E) | |
{ | |
return enum_has_range_behavior<E>::end; | |
} | |
// ----------------------------------------------------------------------------- | |
enum class SwampDangers : uint8_t | |
{ | |
FLAME_SPURTS = 1 << 0, | |
LIGHTNING_SAND = 1 << 1, | |
ROUSES = 1 << 2 | |
}; | |
template <> | |
struct enum_has_flag_behavior<SwampDangers> : true_type {}; | |
bool discoverFlameSpurtNoise() { return true; } | |
bool rescueButtercupFromLightningSand() { return true; } | |
void getAttackedByROUS() | |
{ | |
cout << "Urgh!" << endl; | |
} | |
void navigateFireSwamp() | |
{ | |
SwampDangers d{}; | |
if (discoverFlameSpurtNoise()) | |
d |= SwampDangers::FLAME_SPURTS; | |
if (rescueButtercupFromLightningSand()) | |
d |= SwampDangers::LIGHTNING_SAND; | |
cout << "ROUSes? I don't think they exist." << endl; | |
getAttackedByROUS(); | |
} | |
// ----------------------------------------------------------------------------- | |
enum class InatorButtons : uint8_t | |
{ | |
ON_OFF, | |
SELF_DESTRUCT, | |
MAX | |
}; | |
template <> | |
struct enum_has_range_behavior<InatorButtons> : true_type | |
{ | |
static const InatorButtons begin = InatorButtons::ON_OFF; | |
static const InatorButtons end = InatorButtons::MAX; | |
}; | |
void pressButton(InatorButtons b) | |
{ | |
switch (b) | |
{ | |
case InatorButtons::ON_OFF: | |
cout << "Behold as I unleash my new Inator on the tri-state area!" << endl; | |
break; | |
case InatorButtons::SELF_DESTRUCT: | |
cout << "Curse you, Perry the Platypus!" << endl; | |
break; | |
default: | |
break; | |
} | |
} | |
void pressAllButtons() | |
{ | |
for (auto button : InatorButtons{}) | |
pressButton(button); | |
} | |
// ----------------------------------------------------------------------------- | |
int main(void) | |
{ | |
navigateFireSwamp(); | |
pressAllButtons(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment