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