| /* | |
| * Copyright 2023 Rive | |
| */ | |
| #ifndef _RIVE_ENUM_BITSET_HPP_ | |
| #define _RIVE_ENUM_BITSET_HPP_ | |
| #include <cassert> | |
| #include <type_traits> | |
| namespace rive::enums | |
| { | |
| namespace internal | |
| { | |
| // Helper struct to test whether a type is a scoped enum ("enum class" or | |
| // "enum struct") | |
| template <typename Enum, typename = Enum> | |
| struct IsScopedEnumImpl : std::is_enum<Enum> | |
| {}; | |
| // non-scoped enums are implicitly convertible to integers, which means the | |
| // unary + operator works for them. If the unary + operation works, then the | |
| // given type is actually a scoped enum. | |
| template <typename Enum> | |
| struct IsScopedEnumImpl<Enum, decltype(Enum(+std::declval<Enum>()))> | |
| : std::false_type | |
| {}; | |
| template <typename Enum> | |
| constexpr bool is_scoped_enum = IsScopedEnumImpl<Enum>::value; | |
| // Helper struct to test whether a type is a "flag enum" (which would be a | |
| // scoped enum that has either a "none", "None", or "NONE" element that equals | |
| // 0. | |
| template <typename Enum, typename NoneType = Enum> | |
| struct IsFlagEnumImpl : std::false_type | |
| {}; | |
| template <typename Enum> | |
| struct IsFlagEnumImpl<Enum, decltype(Enum::none)> | |
| : std::integral_constant<bool, is_scoped_enum<Enum>&& int(Enum::none) == 0> | |
| {}; | |
| template <typename Enum> | |
| struct IsFlagEnumImpl<Enum, decltype(Enum::None)> | |
| : std::integral_constant<bool, is_scoped_enum<Enum>&& int(Enum::None) == 0> | |
| {}; | |
| template <typename Enum> | |
| struct IsFlagEnumImpl<Enum, decltype(Enum::NONE)> | |
| : std::integral_constant<bool, is_scoped_enum<Enum>&& int(Enum::NONE) == 0> | |
| {}; | |
| template <typename Enum> | |
| constexpr bool is_flag_enum = IsFlagEnumImpl<Enum>::value; | |
| #define RIVE_ENABLE_IF_IS_SCOPED_ENUM(Enum) \ | |
| template <typename Enum, \ | |
| std::enable_if_t<enums::internal::is_scoped_enum<Enum>, int> = \ | |
| 0> | |
| #define RIVE_ENABLE_IF_IS_FLAG_ENUM(Enum) \ | |
| template <typename Enum, \ | |
| std::enable_if_t<enums::internal::is_flag_enum<Enum>, int> = 0> | |
| } // namespace internal | |
| RIVE_ENABLE_IF_IS_SCOPED_ENUM(Enum) | |
| constexpr std::underlying_type_t<Enum> underlying_value(Enum v) noexcept | |
| { | |
| return std::underlying_type_t<Enum>(v); | |
| } | |
| RIVE_ENABLE_IF_IS_SCOPED_ENUM(Enum) | |
| constexpr Enum incr(Enum v) noexcept { return Enum(underlying_value(v) + 1); } | |
| RIVE_ENABLE_IF_IS_SCOPED_ENUM(Enum) | |
| constexpr Enum decr(Enum v) noexcept { return Enum(underlying_value(v) - 1); } | |
| } // namespace rive::enums | |
| // The operators need to live in the base `rive` namespace so they're | |
| // discoverable from anywhere in the codebase | |
| namespace rive | |
| { | |
| RIVE_ENABLE_IF_IS_FLAG_ENUM(Enum) | |
| inline constexpr Enum operator&(Enum a, Enum b) noexcept | |
| { | |
| return Enum(enums::underlying_value(a) & enums::underlying_value(b)); | |
| } | |
| RIVE_ENABLE_IF_IS_FLAG_ENUM(Enum) | |
| inline constexpr Enum operator^(Enum a, Enum b) noexcept | |
| { | |
| return Enum(enums::underlying_value(a) ^ enums::underlying_value(b)); | |
| } | |
| RIVE_ENABLE_IF_IS_FLAG_ENUM(Enum) | |
| inline constexpr Enum operator|(Enum a, Enum b) noexcept | |
| { | |
| return Enum(enums::underlying_value(a) | enums::underlying_value(b)); | |
| } | |
| RIVE_ENABLE_IF_IS_FLAG_ENUM(Enum) | |
| inline constexpr Enum operator~(Enum a) noexcept | |
| { | |
| return Enum(~enums::underlying_value(a)); | |
| } | |
| RIVE_ENABLE_IF_IS_FLAG_ENUM(Enum) | |
| inline constexpr Enum& operator&=(Enum& a, Enum b) noexcept | |
| { | |
| a = (a & b); | |
| return a; | |
| } | |
| RIVE_ENABLE_IF_IS_FLAG_ENUM(Enum) | |
| inline constexpr Enum& operator^=(Enum& a, Enum b) noexcept | |
| { | |
| a = (a ^ b); | |
| return a; | |
| } | |
| RIVE_ENABLE_IF_IS_FLAG_ENUM(Enum) | |
| inline constexpr Enum& operator|=(Enum& a, Enum b) noexcept | |
| { | |
| a = (a | b); | |
| return a; | |
| } | |
| } // namespace rive | |
| namespace rive::enums | |
| { | |
| RIVE_ENABLE_IF_IS_FLAG_ENUM(Enum) | |
| constexpr bool is_single_flag(Enum flags) noexcept | |
| { | |
| auto u = underlying_value(flags); | |
| return u != 0 && (u & (u - 1)) == 0; | |
| } | |
| RIVE_ENABLE_IF_IS_FLAG_ENUM(Enum) | |
| constexpr bool is_flag_set(Enum flags, Enum testFlag) noexcept | |
| { | |
| assert(is_single_flag(testFlag)); | |
| return (flags & testFlag) != Enum(0); | |
| } | |
| RIVE_ENABLE_IF_IS_FLAG_ENUM(Enum) | |
| constexpr bool any_flag_set(Enum flags) noexcept { return flags != Enum(0); } | |
| RIVE_ENABLE_IF_IS_FLAG_ENUM(Enum) | |
| constexpr bool any_flag_set(Enum flags, Enum mask) noexcept | |
| { | |
| return any_flag_set(flags & mask); | |
| } | |
| RIVE_ENABLE_IF_IS_FLAG_ENUM(Enum) | |
| constexpr bool all_flags_set(Enum flags, Enum mask) noexcept | |
| { | |
| return (flags & mask) == mask; | |
| } | |
| RIVE_ENABLE_IF_IS_FLAG_ENUM(Enum) | |
| constexpr bool no_flags_set(Enum flags) noexcept { return flags == Enum(0); } | |
| RIVE_ENABLE_IF_IS_FLAG_ENUM(Enum) | |
| constexpr bool no_flags_set(Enum flags, Enum mask) noexcept | |
| { | |
| return no_flags_set(flags & mask); | |
| } | |
| } // namespace rive::enums | |
| #endif | |
| #undef RIVE_ENABLE_IF_IS_FLAG_ENUM | |
| #undef RIVE_ENABLE_IF_IS_SCOPED_ENUM |