#pragma once #include <type_traits> #include <utility> namespace util { template <typename E> constexpr auto underlying_type_cast(const E &value) -> std::underlying_type_t<E> requires std::is_enum_v<E> { return static_cast<std::underlying_type_t<E>>(value); } namespace bitflag_operators { template <typename E> concept enum_type = std::is_enum_v<E>; template <enum_type E> constexpr auto operator&(const E &rhs, const E &lhs) -> E { return static_cast<E>(underlying_type_cast(rhs) & underlying_type_cast(lhs)); } template <enum_type E> constexpr auto operator&=(E &rhs, const E &lhs) -> E & { rhs = rhs & lhs; return rhs; } template <enum_type E> constexpr auto operator|(const E &rhs, const E &lhs) -> E { return static_cast<E>(underlying_type_cast(rhs) | underlying_type_cast(lhs)); } template <enum_type E> constexpr auto operator|=(E &rhs, const E &lhs) -> E & { rhs = rhs | lhs; return rhs; } template <enum_type E> constexpr auto operator^(const E &rhs, const E &lhs) -> E { return static_cast<E>(underlying_type_cast(rhs) ^ underlying_type_cast(lhs)); } template <enum_type E> constexpr auto operator^=(E &rhs, const E &lhs) -> E & { rhs = rhs ^ lhs; return rhs; } template <enum_type E> constexpr auto operator~(const E &rhs) -> E { return static_cast<E>(~underlying_type_cast(rhs)); } template <enum_type E> constexpr auto is_empty(const E &value) -> bool { return std::to_underlying(value) == 0; } } // namespace bitflag_operators using bitflag_operators::enum_type; template <typename E> requires std::is_enum_v<E> class EnumFlag { E flags; public: using U = std::underlying_type_t<E>; using value_type = U; constexpr EnumFlag() noexcept : flags(static_cast<E>(U(0))) {} constexpr EnumFlag(const U &value) noexcept : flags(static_cast<E>(value)) {} constexpr EnumFlag(const E &value) noexcept : flags(value) {} constexpr auto operator=(const U &value) -> EnumFlag & { flags = static_cast<E>(value); return *this; } constexpr auto operator=(const E &value) -> EnumFlag & { flags = value; return *this; } constexpr auto as_underlying_type() const -> U { return static_cast<U>(flags); } constexpr auto as_enum_type() const -> E { return flags; } /// `union` is reserved constexpr auto unison(const EnumFlag &other) const -> EnumFlag { return this | other; } constexpr auto intersection(const EnumFlag &other) const -> EnumFlag { return *this & other; } constexpr auto contains(const EnumFlag &other) const -> bool { return intersection(other).as_underlying_type() == other.as_underlying_type(); } constexpr auto intersects(const EnumFlag &other) const -> bool { return intersection(other).as_underlying_type() != 0; } constexpr auto add(const EnumFlag &other) -> EnumFlag & { *this |= other; return *this; } constexpr auto remove(const EnumFlag &other) -> EnumFlag & { *this &= ~other; return *this; } constexpr operator U() const { return as_underlying_type(); } constexpr operator E() const { return as_enum_type(); } // auto operator==(const EnumFlag& other) const { // return as_underlying_type() == other.as_underlying_type(); // } constexpr auto operator<=>(const EnumFlag &other) const { return other.as_underlying_type() <=> other.as_underlying_type(); } // operator bool() const { return as_underlying_type() != 0; } constexpr auto operator|=(const EnumFlag &other) { flags = static_cast<E>(as_underlying_type() | other.as_underlying_type()); } constexpr auto operator|(const EnumFlag &other) const -> EnumFlag { auto flag = *this; flag |= other; return flag; } constexpr auto operator&=(const EnumFlag &other) { flags = static_cast<E>(as_underlying_type() & other.as_underlying_type()); } constexpr auto operator&(const EnumFlag &other) const -> EnumFlag { auto flag = *this; flag &= other; return flag; } constexpr auto operator~() const -> EnumFlag { return static_cast<E>(~as_underlying_type()); } constexpr auto operator^=(const EnumFlag &other) { flags = static_cast<E>(as_underlying_type() ^ other.as_underlying_type()); } constexpr auto operator^(const EnumFlag &other) const -> EnumFlag { auto flag = *this; flag ^= other; return flag; } }; } // namespace util