Skip to content

Instantly share code, notes, and snippets.

@elbeno
Last active November 28, 2016 10:48
Show Gist options
  • Save elbeno/992801884dd13a8ce4bb to your computer and use it in GitHub Desktop.
Save elbeno/992801884dd13a8ce4bb to your computer and use it in GitHub Desktop.
Treating enum class as flags or iterable
#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