From 4d489589659be75113055a3744fd17ddda992684 Mon Sep 17 00:00:00 2001 From: Marc Chevrier Date: Wed, 12 Feb 2025 13:39:19 +0100 Subject: [PATCH] enum_set enhancements, step 3 --- Tests/CMakeLib/testCMExtEnumSet.cxx | 192 +++++++++++++++------------- Utilities/std/cmext/enum_set | 78 +++++++++-- 2 files changed, 171 insertions(+), 99 deletions(-) diff --git a/Tests/CMakeLib/testCMExtEnumSet.cxx b/Tests/CMakeLib/testCMExtEnumSet.cxx index 218b837559..d480be6614 100644 --- a/Tests/CMakeLib/testCMExtEnumSet.cxx +++ b/Tests/CMakeLib/testCMExtEnumSet.cxx @@ -14,24 +14,44 @@ namespace { int failed = 0; +enum class Test : std::uint8_t +{ + A, + B, + C, + D, + E +}; + +using EnumSetTest = cm::enum_set; + +enum class Test2 : std::uint8_t +{ + A, + B, + C, + D, + E +}; + +using EnumSetTest2 = cm::enum_set; +} + +CM_ENUM_SET_TRAITS(EnumSetTest) +CM_ENUM_SET_TRAITS(EnumSetTest2) + +namespace { void testDeclaration() { std::cout << "testDeclaration()" << std::endl; { - enum class Test : std::uint8_t - { - A, - B, - C, - D - }; - cm::enum_set testSet1; - cm::enum_set testSet2 = Test::A; - cm::enum_set testSet3 = Test::A | Test::C; - cm::enum_set testSet4 = Test::A + Test::C; - cm::enum_set testSet5{ Test::A, Test::C }; - cm::enum_set testSet6 = testSet3; + EnumSetTest testSet1; + EnumSetTest testSet2 = Test::A; + EnumSetTest testSet3 = Test::A | Test::C; + EnumSetTest testSet4 = Test::A + Test::C; + EnumSetTest testSet5{ Test::A, Test::C }; + EnumSetTest testSet6 = testSet3; if (!testSet1.empty()) { ++failed; @@ -48,15 +68,50 @@ void testDeclaration() } } { - enum class Test : std::uint8_t - { - A, - B, - C, - D - }; - cm::enum_set testSet1; - cm::enum_set testSet2; + EnumSetTest2 testSet1; + EnumSetTest2 testSet2 = Test2::A; + EnumSetTest2 testSet3 = Test2::A | Test2::C; + EnumSetTest2 testSet4 = Test2::A + Test2::C; + EnumSetTest2 testSet5{ Test2::A, Test2::C }; + EnumSetTest2 testSet6 = testSet3; + + if (!testSet1.empty()) { + ++failed; + } + if (testSet2.size() != 1) { + ++failed; + } + if (testSet3.size() != 2 || testSet4.size() != 2 || testSet5.size() != 2 || + testSet6.size() != 2) { + ++failed; + } + if (testSet3 != testSet4 || testSet4 != testSet5 || testSet5 != testSet6) { + ++failed; + } + } + { + using LocalEnumSetTest = cm::enum_set; + LocalEnumSetTest testSet1; + LocalEnumSetTest testSet2 = Test::A; + LocalEnumSetTest testSet3{ Test::A, Test::C }; + LocalEnumSetTest testSet4 = testSet3; + + if (!testSet1.empty()) { + ++failed; + } + if (testSet2.size() != 1) { + ++failed; + } + if (testSet3.size() != 2 || testSet4.size() != 2) { + ++failed; + } + if (testSet3 != testSet4) { + ++failed; + } + } + { + EnumSetTest testSet1; + EnumSetTest2 testSet2; if (testSet1.size() != 0 || testSet1.max_size() != @@ -64,7 +119,7 @@ void testDeclaration() typename std::underlying_type::type>::digits) { ++failed; } - if (testSet2.size() != 0 || testSet2.max_size() != 4) { + if (testSet2.size() != 0 || testSet2.max_size() != 5) { ++failed; } } @@ -74,14 +129,7 @@ void testIteration() { std::cout << "testIteration()" << std::endl; - enum class Test : std::uint8_t - { - A, - B, - C, - D - }; - cm::enum_set testSet{ Test::A, Test::C, Test::B }; + EnumSetTest2 testSet{ Test2::A, Test2::C, Test2::B }; if (testSet.size() != 3) { ++failed; @@ -112,17 +160,8 @@ void testEdition() { std::cout << "testEdition()" << std::endl; - enum class Test : std::uint8_t { - A, - B, - C, - D, - E - }; - - { - cm::enum_set testSet{ Test::A, Test::C, Test::B }; + EnumSetTest testSet{ Test::A, Test::C, Test::B }; auto pos = testSet.insert(Test::E); if (!pos.second || testSet.size() != 4 || *(pos.first) != Test::E || @@ -144,7 +183,7 @@ void testEdition() } } { - cm::enum_set testSet{ Test::A, Test::C, Test::B }; + EnumSetTest testSet{ Test::A, Test::C, Test::B }; testSet += { Test::D, Test::E }; @@ -173,8 +212,8 @@ void testEdition() } } { - cm::enum_set testSet1{ Test::A, Test::C, Test::B }; - cm::enum_set testSet2{ Test::A, Test::D, Test::E }; + EnumSetTest testSet1{ Test::A, Test::C, Test::B }; + EnumSetTest testSet2{ Test::A, Test::D, Test::E }; testSet1.insert(testSet2.cbegin(), testSet2.cend()); std::set reference{ static_cast(Test::A), @@ -204,8 +243,8 @@ void testEdition() } } { - cm::enum_set testSet1{ Test::A, Test::C, Test::B }; - cm::enum_set testSet2{ Test::C, Test::E }; + EnumSetTest testSet1{ Test::A, Test::C, Test::B }; + EnumSetTest testSet2{ Test::C, Test::E }; testSet1.flip(Test::A); if (testSet1.size() != 2 || testSet1.contains(Test::A)) { @@ -224,7 +263,7 @@ void testEdition() } } { - cm::enum_set testSet1; + EnumSetTest testSet1; auto testSet2 = Test::A + Test::C + Test::B; testSet1.set({ Test::A, Test::C, Test::B }); @@ -264,39 +303,38 @@ void testEdition() } } { - using ESet = cm::enum_set; - ESet testSet1; - ESet testSet2{ Test::A, Test::C, Test::B }; + EnumSetTest2 testSet1; + EnumSetTest2 testSet2{ Test2::A, Test2::C, Test2::B }; testSet1.set(); if (testSet1.size() != 5 || testSet1.size() != testSet1.max_size()) { ++failed; } - testSet1.flip({ Test::D, Test::E }); + testSet1.flip({ Test2::D, Test2::E }); if (testSet1.size() != 3 || testSet1 != testSet2) { ++failed; } - testSet1.flip(Test::D | Test::E); - testSet2 += Test::D + Test::E; + testSet1.flip(Test2::D | Test2::E); + testSet2 += Test2::D + Test2::E; if (testSet1.size() != 5 || testSet1 != testSet2) { ++failed; } - testSet1.flip(Test::E); - testSet2 -= Test::E; + testSet1.flip(Test2::E); + testSet2 -= Test2::E; if (testSet1.size() != 4 || testSet1 != testSet2) { ++failed; } - testSet1 ^= { Test::A, Test::B, Test::E, Test::D }; - testSet2 = { Test::C, Test::E }; + testSet1 ^= { Test2::A, Test2::B, Test2::E, Test2::D }; + testSet2 = { Test2::C, Test2::E }; if (testSet1.size() != 2 || testSet1 != testSet2) { ++failed; } - testSet1 ^= { Test::A, Test::B, Test::E }; - testSet2 = { Test::A, Test::B, Test::C }; + testSet1 ^= { Test2::A, Test2::B, Test2::E }; + testSet2 = { Test2::A, Test2::B, Test2::C }; if (testSet1.size() != 3 || testSet1 != testSet2) { ++failed; } - testSet2 = Test::A | Test::B | Test::C; + testSet2 = Test2::A | Test2::B | Test2::C; if (testSet1.size() != 3 || testSet1 != testSet2) { ++failed; } @@ -308,15 +346,7 @@ void testChecks() std::cout << "testChecks()" << std::endl; { - enum class Test : std::uint8_t - { - A, - B, - C, - D - }; - - cm::enum_set testSet; + EnumSetTest testSet; if (!testSet.empty()) { ++failed; @@ -340,15 +370,7 @@ void testChecks() } } { - enum class Test : std::uint8_t - { - A, - B, - C, - D - }; - - cm::enum_set testSet; + EnumSetTest2 testSet; if (!testSet.none()) { ++failed; @@ -357,7 +379,7 @@ void testChecks() ++failed; } - testSet = Test::A; + testSet = Test2::A; if (!testSet.any() || testSet.none() || testSet.all()) { ++failed; } @@ -368,16 +390,8 @@ void testChecks() } } { - enum class Test : std::uint8_t - { - A, - B, - C, - D - }; - - cm::enum_set testSet1; - cm::enum_set testSet2{ Test::A, Test::C }; + EnumSetTest testSet1; + EnumSetTest testSet2{ Test::A, Test::C }; if (!testSet1.none_of(testSet2) || testSet1.any_of(testSet2) || testSet1.all_of(testSet2)) { diff --git a/Utilities/std/cmext/enum_set b/Utilities/std/cmext/enum_set index f1d615ab2e..d39f07b228 100644 --- a/Utilities/std/cmext/enum_set +++ b/Utilities/std/cmext/enum_set @@ -561,21 +561,79 @@ inline void erase_if(enum_set& set, Predicate pred) } // namespace cm // -// WARNING: the following two functions rely on an enum_set without -// explicit size. +// WARNING: the following two operators rely on the enum_set_traits +// struct definition. +// The macro CM_ENUM_SET_TRAITS(EnumSet) can be used to define this structure. // -// TODO: ensure compatibility with any enum_set definitions. +// Notes: +// When CM_ENUM_SET_TRAITS is used, the following restrictions applies: +// * Due to language constraints, the enum_set_traits specialization must +// occur outside of any namespace or function definition. +// * Only one enum_set instantiation is supported per enum class type. // -template ::value, int> = 0> -inline cm::enum_set operator+(Enum lhs, Enum rhs) + +template +struct cm_enum_set_traits { - return cm::enum_set{ lhs, rhs }; +}; + +namespace cm { +template > +struct is_enum_set : std::false_type +{ +}; +template +struct is_enum_set::type>> + : std::true_type +{ +}; +} + +#if defined(__SUNPRO_CC) && defined(__sparc) +// Oracle DeveloperStudio C++ compiler on Solaris/Sparc crash on the following +// template declarations, so declare explicitly the operators. + +// Helper macro to define the enum_set_traits struct specialization. +# define CM_ENUM_SET_TRAITS(E) \ + template <> \ + struct cm_enum_set_traits \ + { \ + using type = E; \ + using value_type = E::value_type; \ + }; \ + \ + inline E operator+(E::value_type lhs, E::value_type rhs) \ + { \ + return { lhs, rhs }; \ + } \ + \ + inline E operator|(E::value_type lhs, E::value_type rhs) \ + { \ + return { lhs, rhs }; \ + } + +#else + +// Helper macro to define the enum_set_traits struct specialization. +# define CM_ENUM_SET_TRAITS(E) \ + template <> \ + struct cm_enum_set_traits \ + { \ + using type = E; \ + using value_type = E::value_type; \ + }; + +template ::value, int> = 0> +inline typename cm_enum_set_traits::type operator+(Enum lhs, Enum rhs) +{ + return { lhs, rhs }; } // Alternate syntax template ::value, int> = 0> -inline cm::enum_set operator|(Enum lhs, Enum rhs) + typename cm::enable_if_t::value, int> = 0> +inline typename cm_enum_set_traits::type operator|(Enum lhs, Enum rhs) { - return cm::enum_set{ lhs, rhs }; + return { lhs, rhs }; } +#endif