#pragma once #include #include namespace util { template constexpr auto underlying_type_cast(const E &value) -> std::underlying_type_t requires std::is_enum_v { return static_cast>(value); } namespace bitflag_operators { template concept enum_type = std::is_enum_v; template constexpr auto operator&(const E &rhs, const E &lhs) -> E { return static_cast(underlying_type_cast(rhs) & underlying_type_cast(lhs)); } template constexpr auto operator&=(E &rhs, const E &lhs) -> E & { rhs = rhs & lhs; return rhs; } template constexpr auto operator|(const E &rhs, const E &lhs) -> E { return static_cast(underlying_type_cast(rhs) | underlying_type_cast(lhs)); } template constexpr auto operator|=(E &rhs, const E &lhs) -> E & { rhs = rhs | lhs; return rhs; } template constexpr auto operator^(const E &rhs, const E &lhs) -> E { return static_cast(underlying_type_cast(rhs) ^ underlying_type_cast(lhs)); } template constexpr auto operator^=(E &rhs, const E &lhs) -> E & { rhs = rhs ^ lhs; return rhs; } template constexpr auto operator~(const E &rhs) -> E { return static_cast(~underlying_type_cast(rhs)); } template constexpr auto is_empty(const E &value) -> bool { return std::to_underlying(value) == 0; } } // namespace bitflag_operators using bitflag_operators::enum_type; template requires std::is_enum_v class EnumFlag { E flags; public: using U = std::underlying_type_t; using value_type = U; constexpr EnumFlag() noexcept : flags(static_cast(U(0))) {} constexpr EnumFlag(const U &value) noexcept : flags(static_cast(value)) {} constexpr EnumFlag(const E &value) noexcept : flags(value) {} constexpr auto operator=(const U &value) -> EnumFlag & { flags = static_cast(value); return *this; } constexpr auto operator=(const E &value) -> EnumFlag & { flags = value; return *this; } constexpr auto as_underlying_type() const -> U { return static_cast(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(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(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(~as_underlying_type()); } constexpr auto operator^=(const EnumFlag &other) { flags = static_cast(as_underlying_type() ^ other.as_underlying_type()); } constexpr auto operator^(const EnumFlag &other) const -> EnumFlag { auto flag = *this; flag ^= other; return flag; } }; } // namespace util