diff --git a/include/fixed_containers/enum_utils.hpp b/include/fixed_containers/enum_utils.hpp index 93eecea2..4184db25 100644 --- a/include/fixed_containers/enum_utils.hpp +++ b/include/fixed_containers/enum_utils.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -364,14 +365,59 @@ class SkeletalRichEnumStorageBase +class RichEnumConstantProxy +{ +private: + using BackingEnum = typename RichEnumType::BackingEnum; + + BackingEnum backing_enum_{}; + +public: + explicit(false) constexpr RichEnumConstantProxy(const BackingEnum& backing_enum) + : backing_enum_{backing_enum} + { + } + + constexpr RichEnumConstantProxy(const RichEnumConstantProxy& original) noexcept = delete; + constexpr RichEnumConstantProxy(RichEnumConstantProxy&& RichEnumConstantProxy) noexcept = + delete; + constexpr RichEnumConstantProxy& operator=(const RichEnumConstantProxy& RichEnumConstantProxy) = + delete; + constexpr RichEnumConstantProxy& operator=(RichEnumConstantProxy&& original) = delete; + + constexpr const RichEnumType& get() const + { + return ::fixed_containers::rich_enums_detail::value_of(backing_enum_) + .value() + .get(); + } + + explicit(false) constexpr operator BackingEnum() const { return get(); } + explicit(false) constexpr operator const RichEnumType&() const { return get(); } + + // TRANSITION + constexpr const RichEnumType& operator()() const { return get(); } + + constexpr bool operator==(const RichEnumType& other) const { return get() == other; } + constexpr std::strong_ordering operator<=>(const RichEnumType& other) const + { + return get() <=> other; + } + + constexpr const RichEnumType* operator->() const { return std::addressof(get()); } + constexpr const RichEnumType& operator*() const noexcept { return get(); } + constexpr const RichEnumType* operator&() const noexcept // NOLINT(google-runtime-operator) + { + return std::addressof(get()); + } +}; + // MACRO to reduce four lines into one and avoid bugs from potential discrepancy between the // BackingEnum::ENUM_CONSTANT and the rich enum ENUM_CONSTANT() // Must be used after the values() static function is declared in the rich enum. #define FIXED_CONTAINERS_RICH_ENUM_CONSTANT_GEN_HELPER(RichEnumName, CONSTANT_NAME) \ - static constexpr const RichEnumName& CONSTANT_NAME() \ - { \ - return RichEnumName::value_of(BackingEnum::CONSTANT_NAME).value(); \ - } + static constexpr RichEnumConstantProxy CONSTANT_NAME{BackingEnum::CONSTANT_NAME}; namespace fixed_containers::rich_enums { diff --git a/test/enum_utils_test.cpp b/test/enum_utils_test.cpp index 9367a891..40fcaa58 100644 --- a/test/enum_utils_test.cpp +++ b/test/enum_utils_test.cpp @@ -25,14 +25,14 @@ static_assert(IsRichEnumStorage>); TEST(RichEnum, DirectFieldAccess) { static_assert( - consteval_compare::equaldetail_backing_enum.val, TestRichEnum1BackingEnum::C_ONE>); } TEST(RichEnum, DirectFieldAccessBool) { static_assert( - consteval_compare::equaldetail_backing_enum.val, TestRichEnumBoolBackingEnum::TRUE_VALUE>); } @@ -159,10 +159,10 @@ TEST(BuiltinEnumAdapter, Ordinal) TEST(RichEnumAdapter, Ordinal) { static_assert(4 == EnumAdapter::count()); - static_assert(0 == EnumAdapter::ordinal(TestRichEnum1::C_ONE())); - static_assert(1 == EnumAdapter::ordinal(TestRichEnum1::C_TWO())); - static_assert(2 == EnumAdapter::ordinal(TestRichEnum1::C_THREE())); - static_assert(3 == EnumAdapter::ordinal(TestRichEnum1::C_FOUR())); + static_assert(0 == EnumAdapter::ordinal(TestRichEnum1::C_ONE)); + static_assert(1 == EnumAdapter::ordinal(TestRichEnum1::C_TWO)); + static_assert(2 == EnumAdapter::ordinal(TestRichEnum1::C_THREE)); + static_assert(3 == EnumAdapter::ordinal(TestRichEnum1::C_FOUR)); } TEST(SpecializedEnumAdapter, Ordinal) @@ -190,10 +190,10 @@ TEST(BuiltinEnumAdapter, ToString) TEST(RichEnumAdapter, ToString) { static_assert(4 == EnumAdapter::count()); - static_assert("C_ONE" == EnumAdapter::to_string(TestRichEnum1::C_ONE())); - static_assert("C_TWO" == EnumAdapter::to_string(TestRichEnum1::C_TWO())); - static_assert("C_THREE" == EnumAdapter::to_string(TestRichEnum1::C_THREE())); - static_assert("C_FOUR" == EnumAdapter::to_string(TestRichEnum1::C_FOUR())); + static_assert("C_ONE" == EnumAdapter::to_string(TestRichEnum1::C_ONE)); + static_assert("C_TWO" == EnumAdapter::to_string(TestRichEnum1::C_TWO)); + static_assert("C_THREE" == EnumAdapter::to_string(TestRichEnum1::C_THREE)); + static_assert("C_FOUR" == EnumAdapter::to_string(TestRichEnum1::C_FOUR)); } TEST(SpecializedEnumAdapter, ToString) @@ -208,35 +208,35 @@ TEST(SpecializedEnumAdapter, ToString) TEST(RichEnum, Ordinal) { { - static_assert(TestRichEnum1::C_ONE().ordinal() == 0); - static_assert(TestRichEnum1::C_TWO().ordinal() == 1); - static_assert(TestRichEnum1::C_THREE().ordinal() == 2); - static_assert(TestRichEnum1::C_FOUR().ordinal() == 3); + static_assert(TestRichEnum1::C_ONE->ordinal() == 0); + static_assert(TestRichEnum1::C_TWO->ordinal() == 1); + static_assert(TestRichEnum1::C_THREE->ordinal() == 2); + static_assert(TestRichEnum1::C_FOUR->ordinal() == 3); } { - static_assert(TestRichEnum2::C_ONE().ordinal() == 0); - static_assert(TestRichEnum2::C_TWO().ordinal() == 1); - static_assert(TestRichEnum2::C_THREE().ordinal() == 2); - static_assert(TestRichEnum2::C_FOUR().ordinal() == 3); + static_assert(TestRichEnum2::C_ONE->ordinal() == 0); + static_assert(TestRichEnum2::C_TWO->ordinal() == 1); + static_assert(TestRichEnum2::C_THREE->ordinal() == 2); + static_assert(TestRichEnum2::C_FOUR->ordinal() == 3); } } TEST(RichEnum, ValueOfName) { { - static_assert(TestRichEnum1::value_of("C_ONE") == TestRichEnum1::C_ONE()); - static_assert(TestRichEnum1::value_of("C_TWO") == TestRichEnum1::C_TWO()); - static_assert(TestRichEnum1::value_of("C_THREE") == TestRichEnum1::C_THREE()); - static_assert(TestRichEnum1::value_of("C_FOUR") == TestRichEnum1::C_FOUR()); + static_assert(TestRichEnum1::value_of("C_ONE") == TestRichEnum1::C_ONE); + static_assert(TestRichEnum1::value_of("C_TWO") == TestRichEnum1::C_TWO); + static_assert(TestRichEnum1::value_of("C_THREE") == TestRichEnum1::C_THREE); + static_assert(TestRichEnum1::value_of("C_FOUR") == TestRichEnum1::C_FOUR); static_assert(TestRichEnum1::value_of("INVALID") == std::nullopt); } { constexpr const TestRichEnum1& MY_VALUE = TestRichEnum1::value_of("C_ONE").value(); - static_assert(MY_VALUE == TestRichEnum1::C_ONE()); - static_assert(&MY_VALUE == &TestRichEnum1::C_ONE()); + static_assert(MY_VALUE == TestRichEnum1::C_ONE); + static_assert(&MY_VALUE == &TestRichEnum1::C_ONE); } } @@ -244,10 +244,10 @@ TEST(RichEnum, ValueOfBackingEnum) { { using BE = detail::TestRichEnum1BackingEnum; - static_assert(TestRichEnum1::value_of(BE::C_ONE) == TestRichEnum1::C_ONE()); - static_assert(TestRichEnum1::value_of(BE::C_TWO) == TestRichEnum1::C_TWO()); - static_assert(TestRichEnum1::value_of(BE::C_THREE) == TestRichEnum1::C_THREE()); - static_assert(TestRichEnum1::value_of(BE::C_FOUR) == TestRichEnum1::C_FOUR()); + static_assert(TestRichEnum1::value_of(BE::C_ONE) == TestRichEnum1::C_ONE); + static_assert(TestRichEnum1::value_of(BE::C_TWO) == TestRichEnum1::C_TWO); + static_assert(TestRichEnum1::value_of(BE::C_THREE) == TestRichEnum1::C_THREE); + static_assert(TestRichEnum1::value_of(BE::C_FOUR) == TestRichEnum1::C_FOUR); static_assert(TestRichEnum1::value_of(static_cast(29)) == std::nullopt); } @@ -255,26 +255,26 @@ TEST(RichEnum, ValueOfBackingEnum) using BE = detail::TestRichEnum1BackingEnum; constexpr const TestRichEnum1& MY_VALUE = TestRichEnum1::value_of(BE::C_ONE).value(); - static_assert(MY_VALUE == TestRichEnum1::C_ONE()); - static_assert(&MY_VALUE == &TestRichEnum1::C_ONE()); + static_assert(MY_VALUE == TestRichEnum1::C_ONE); + static_assert(&MY_VALUE == &TestRichEnum1::C_ONE); } } TEST(RichEnum, ValueOfUnderlyingInt) { { - static_assert(TestRichEnum1::value_of(19) == TestRichEnum1::C_ONE()); - static_assert(TestRichEnum1::value_of(21) == TestRichEnum1::C_TWO()); - static_assert(TestRichEnum1::value_of(23) == TestRichEnum1::C_THREE()); - static_assert(TestRichEnum1::value_of(25) == TestRichEnum1::C_FOUR()); + static_assert(TestRichEnum1::value_of(19) == TestRichEnum1::C_ONE); + static_assert(TestRichEnum1::value_of(21) == TestRichEnum1::C_TWO); + static_assert(TestRichEnum1::value_of(23) == TestRichEnum1::C_THREE); + static_assert(TestRichEnum1::value_of(25) == TestRichEnum1::C_FOUR); static_assert(TestRichEnum1::value_of(29) == std::nullopt); } { constexpr const TestRichEnum1& MY_VALUE = TestRichEnum1::value_of(19).value(); - static_assert(MY_VALUE == TestRichEnum1::C_ONE()); - static_assert(&MY_VALUE == &TestRichEnum1::C_ONE()); + static_assert(MY_VALUE == TestRichEnum1::C_ONE); + static_assert(&MY_VALUE == &TestRichEnum1::C_ONE); } } @@ -292,23 +292,23 @@ TEST(RichEnum, HasValue) constexpr TestRichEnum1 INVALID{}; static_assert(!INVALID.has_value()); static_assert(INVALID == TestRichEnum1{}); - static_assert(INVALID != TestRichEnum1::C_ONE()); - static_assert(INVALID != TestRichEnum1::C_TWO()); - static_assert(INVALID != TestRichEnum1::C_THREE()); - static_assert(INVALID != TestRichEnum1::C_FOUR()); + static_assert(INVALID != TestRichEnum1::C_ONE); + static_assert(INVALID != TestRichEnum1::C_TWO); + static_assert(INVALID != TestRichEnum1::C_THREE); + static_assert(INVALID != TestRichEnum1::C_FOUR); } TEST(RichEnum, BoolNegate) { { - constexpr const TestRichEnumBool& F_VALUE = TestRichEnumBool::FALSE_VALUE(); + constexpr const TestRichEnumBool& F_VALUE = TestRichEnumBool::FALSE_VALUE; static_assert(F_VALUE.has_value()); - static_assert((!F_VALUE) == TestRichEnumBool::TRUE_VALUE()); + static_assert((!F_VALUE) == TestRichEnumBool::TRUE_VALUE); } { - constexpr const TestRichEnumBool& T_VALUE = TestRichEnumBool::TRUE_VALUE(); + constexpr const TestRichEnumBool& T_VALUE = TestRichEnumBool::TRUE_VALUE; static_assert(T_VALUE.has_value()); - static_assert((!T_VALUE) == TestRichEnumBool::FALSE_VALUE()); + static_assert((!T_VALUE) == TestRichEnumBool::FALSE_VALUE); } } @@ -329,8 +329,8 @@ constexpr void rich_enum_constants_can_be_used_as_a_template_parameter() TEST(RichEnum, UsageAsTemplateParameter) { - rich_enum_constants_can_be_used_as_a_template_parameter(); - const RichEnumConstantsCanBeUsedAsATemplateParameter my_struct{}; + rich_enum_constants_can_be_used_as_a_template_parameter(); + const RichEnumConstantsCanBeUsedAsATemplateParameter my_struct{}; static_cast(my_struct); } @@ -340,16 +340,16 @@ TEST(RichEnum, UsageInSwitchCase) { switch (val) { - case TestRichEnum1::C_ONE(): + case TestRichEnum1::C_ONE: return 11; - case TestRichEnum1::C_TWO(): + case TestRichEnum1::C_TWO: return 22; - case TestRichEnum1::C_THREE(): + case TestRichEnum1::C_THREE: return 33; - case TestRichEnum1::C_FOUR(): + case TestRichEnum1::C_FOUR: return 44; } - }(TestRichEnum1::C_TWO()); + }(TestRichEnum1::C_TWO); static_assert(22 == result); }