Add flaggable goodies
This is a good candidate for insertion into Mittelib
#include <iostream>
template <class Enum>
struct flaggable_configuration {
static_assert(std::is_enum_v<Enum>);
static constexpr Enum default_value = Enum{};
static constexpr bool allow_bool_cast = true;
static constexpr bool false_value = default_value;
};
template<class Enum>
struct flaggable {
static_assert(std::is_enum_v<Enum>);
using enum_type = Enum;
using underlying_type = std::underlying_type_t<enum_type>;
static constexpr Enum default_value = flaggable_configuration<Enum>::default_value;
underlying_type raw = static_cast<underlying_type>(default_value);
flaggable() = default;
flaggable(enum_type e) : raw{static_cast<underlying_type>(e)} {}
explicit flaggable(underlying_type raw_) : raw{raw_} {}
template <class Alias = Enum, class = std::enable_if_t<flaggable_configuration<Alias>::allow_bool_cast>>
explicit operator bool() const {
return raw != static_cast<underlying_type>(flaggable_configuration<Alias>::false_value);
}
};
namespace traits {
template <class T, class U>
struct deduce_flaggable_enum {};
template <class Enum>
struct deduce_flaggable_enum<Enum, Enum> {
using type = typename std::enable_if_t<std::is_enum_v<Enum>, flaggable<Enum>>;
};
template <class Enum>
struct deduce_flaggable_enum<Enum, flaggable<Enum>> {
using type = flaggable<Enum>;
};
template <class Enum>
struct deduce_flaggable_enum<flaggable<Enum>, Enum> {
using type = flaggable<Enum>;
};
template <class Enum>
struct deduce_flaggable_enum<flaggable<Enum>, flaggable<Enum>> {
using type = flaggable<Enum>;
};
template <class T, class U>
using deduce_flaggable_enum_t = typename deduce_flaggable_enum<T, U>::type;
}
template<class T, class U, class Flaggable = traits::deduce_flaggable_enum_t<T, U>>
Flaggable operator|(T l, U r) {
if constexpr (std::is_same_v<T, Flaggable> and std::is_same_v<U, Flaggable>) {
return Flaggable{static_cast<typename Flaggable::underlying_type>(l.raw | r.raw)};
} else {
return static_cast<Flaggable>(l) | static_cast<Flaggable>(r);
}
}
template<class T, class U, class Flaggable = traits::deduce_flaggable_enum_t<T, U>>
Flaggable operator&(T l, U r) {
if constexpr (std::is_same_v<T, Flaggable> and std::is_same_v<U, Flaggable>) {
return Flaggable{static_cast<typename Flaggable::underlying_type>(l.raw & r.raw)};
} else {
return static_cast<Flaggable>(l) & static_cast<Flaggable>(r);
}
}
template<class T, class U, class Flaggable = traits::deduce_flaggable_enum_t<T, U>>
Flaggable operator^(T l, U r) {
if constexpr (std::is_same_v<T, Flaggable> and std::is_same_v<U, Flaggable>) {
return Flaggable{static_cast<typename Flaggable::underlying_type>(l.raw ^ r.raw)};
} else {
return static_cast<Flaggable>(l) ^ static_cast<Flaggable>(r);
}
}
template<class T, class Flaggable = traits::deduce_flaggable_enum_t<T, T>>
Flaggable operator~(T l) {
if constexpr (std::is_same_v<T, Flaggable>) {
return Flaggable{static_cast<typename Flaggable::underlying_type>(~l.raw)};
} else {
return ~static_cast<Flaggable>(l);
}
}
enum struct foo : std::uint8_t {
a = 0x1,
b = 0x2,
};
int main() {
std::cout << (foo::a | foo::b).raw << std::endl;
std::cout << ((foo::a & foo::b) | foo::a).raw << std::endl;
std::cout << (foo::a & (foo::a | foo::b)).raw << std::endl;
return 0;
}