diff --git a/Tests/CMakeLib/testCMExtEnumSet.cxx b/Tests/CMakeLib/testCMExtEnumSet.cxx index ecf6d1163b..c08ea113fa 100644 --- a/Tests/CMakeLib/testCMExtEnumSet.cxx +++ b/Tests/CMakeLib/testCMExtEnumSet.cxx @@ -3,7 +3,9 @@ #include #include #include +#include #include +#include #include #include @@ -16,25 +18,70 @@ 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, Test::C }; - cm::enum_set testSet3 = testSet2; + 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; - if (!testSet1.empty()) { - ++failed; + 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; + } } - if (testSet2.size() != 2) { - ++failed; + { + enum class Test : std::uint8_t + { + A, + B, + C, + D + }; + cm::enum_set testSet1; + cm::enum_set testSet2; + + if (testSet1.size() != 0 || + testSet1.max_size() != + std::numeric_limits< + typename std::underlying_type::type>::digits) { + ++failed; + } + if (testSet2.size() != 0 || testSet2.max_size() != 4) { + ++failed; + } } - if (testSet3.size() != 2) { - ++failed; + { + enum class Test : std::uint8_t + { + A, + B, + C, + D, + cm_count = D + }; + cm::enum_set testSet1; + + if (testSet1.size() != 0 || testSet1.max_size() != 4) { + ++failed; + } } } @@ -47,7 +94,8 @@ void testIteration() A, B, C, - D + D, + cm_count = D }; cm::enum_set testSet{ Test::A, Test::C, Test::B }; @@ -86,7 +134,8 @@ void testEdition() B, C, D, - E + E, + cm_count = E }; { @@ -193,12 +242,169 @@ void testEdition() } { cm::enum_set testSet1; - cm::enum_set testSet2{ Test::A, Test::C, Test::B }; + auto testSet2 = Test::A + Test::C + Test::B; + testSet1.set({ Test::A, Test::C, Test::B }); + if (testSet1.size() != 3 || testSet1 != testSet2) { + ++failed; + } + testSet1.reset(); + testSet1.set(Test::A | Test::C | Test::B); + if (testSet1.size() != 3 || testSet1 != testSet2) { + ++failed; + } + testSet1.reset(); + testSet1.set(Test::A + Test::C + Test::B); + if (testSet1.size() != 3 || testSet1 != testSet2) { + ++failed; + } + testSet1.reset(); testSet1 = { Test::A, Test::C, Test::B }; if (testSet1.size() != 3 || testSet1 != testSet2) { ++failed; } + testSet1.reset(); + testSet1 = Test::A | Test::C | Test::B; + if (testSet1.size() != 3 || testSet1 != testSet2) { + ++failed; + } + testSet1.clear(); + testSet1 = Test::A + Test::C + Test::B; + if (testSet1.size() != 3 || testSet1 != testSet2) { + ++failed; + } + testSet1.clear(); + testSet1 |= Test::A; + testSet1 |= Test::C | Test::B; + if (testSet1.size() != 3 || testSet1 != testSet2) { + ++failed; + } + } + { + cm::enum_set testSet1; + cm::enum_set testSet2{ Test::A, Test::C, Test::B }; + + testSet1.set(); + if (testSet1.size() != 5 || testSet1.size() != testSet1.max_size()) { + ++failed; + } + testSet1.flip(Test::D | Test::E); + if (testSet1.size() != 3 || testSet1 != testSet2) { + ++failed; + } + testSet1.flip(Test::D); + testSet2 += Test::D; + if (testSet1.size() != 4 || testSet1 != testSet2) { + ++failed; + } + testSet1 ^= { Test::A, Test::B, Test::E, Test::D }; + testSet2 = Test::C + Test::E; + if (testSet1.size() != 2 || testSet1 != testSet2) { + ++failed; + } + testSet1 ^= Test::A | Test::B | Test::E; + testSet2 = { Test::A, Test::B, Test::C }; + if (testSet1.size() != 3 || testSet1 != testSet2) { + ++failed; + } + } +} + +void testChecks() +{ + std::cout << "testChecks()" << std::endl; + + { + enum class Test : std::uint8_t + { + A, + B, + C, + D, + cm_count = D + }; + + cm::enum_set testSet; + + if (!testSet.empty()) { + ++failed; + } + + testSet = Test::A; + if (testSet.empty()) { + ++failed; + } + if (!testSet) { + ++failed; + } + if (!testSet.contains(Test::A)) { + ++failed; + } + if (testSet.find(Test::A) == testSet.end()) { + ++failed; + } + if (testSet.find(Test::C) != testSet.end()) { + ++failed; + } + } + { + enum class Test : std::uint8_t + { + A, + B, + C, + D, + cm_count = D + }; + + cm::enum_set testSet; + + if (!testSet.none()) { + ++failed; + } + if (testSet.any() || testSet.all()) { + ++failed; + } + + testSet = Test::A; + if (!testSet.any() || testSet.none() || testSet.all()) { + ++failed; + } + + testSet.set(); + if (!testSet.all() || !testSet.any() || testSet.none()) { + ++failed; + } + } + { + enum class Test : std::uint8_t + { + A, + B, + C, + D, + cm_count = D + }; + + cm::enum_set testSet1; + cm::enum_set testSet2{ Test::A, Test::C }; + + if (!testSet1.none_of(testSet2) || testSet1.any_of(testSet2) || + testSet1.all_of(testSet2)) { + ++failed; + } + + testSet1 = Test::A | Test::D; + if (testSet1.none_of(testSet2) || !testSet1.any_of(testSet2) || + testSet1.all_of(testSet2)) { + ++failed; + } + + testSet1 |= Test::C; + if (testSet1.none_of(testSet2) || !testSet1.any_of(testSet2) || + !testSet1.all_of(testSet2)) { + ++failed; + } } } } @@ -208,6 +414,7 @@ int testCMExtEnumSet(int /*unused*/, char* /*unused*/[]) testDeclaration(); testIteration(); testEdition(); + testChecks(); return failed; } diff --git a/Utilities/std/cmext/enum_set b/Utilities/std/cmext/enum_set index 4f1d9fc1aa..f851d29eb9 100644 --- a/Utilities/std/cmext/enum_set +++ b/Utilities/std/cmext/enum_set @@ -15,19 +15,53 @@ #include // -// Class enum_set offers the capability to manage a set of enum values -// Only 'enum class' with unsigned base type are supported. +// Class enum_set offers the capability to manage a set of enum values. +// Only the 'enum class' type with unsigned base type is supported. Moreover, +// all definitions must be specified without a value. // // The methods offered by 'enum_set' are close as possible to the 'std::set' -// container plus some useful methods from 'std::bitset' like 'flip'. +// container as well as the methods from 'std::bitset'. // // Internally, this class use 'std::bitset' container to manage the -// set of enum. The size of the bitset is deduced from the underlying type of -// the enum. +// set of enum. +// +// The size of the bitset is deduced from the underlying type of +// the enum or can be set explicitly as template parameter: +// +// enum class Example : unsigned { A, B, C, D }; +// using ExampleSet = enum_set; +// +// Another possibility is to add, at the end of the list, the definition +// 'cm_count' with the value of the precedent definition: +// +// enum class Example : unsigned { A, B, C, D, cm_count = D }; +// using ExampleSet = enum_set; +// +// To facilitate the usage of the enum_set, operators '+' and '|' can be used +// as alternate to the 'initializer_list': +// +// auto set1 = Example::A | Example::B | Example::C; +// auto set2 = Example::A + Example::B; +// set2.set(Example::C | Example::D); // namespace cm { +namespace internals { +template +struct enum_size +{ + static constexpr auto value = + std::numeric_limits::type>::digits; +}; +template +struct enum_size> +{ + static constexpr auto value = + static_cast::type>(Enum::cm_count) + 1; +}; +} + template class enum_set_iterator { @@ -110,9 +144,9 @@ private: }; template < - typename Enum, + typename Enum, std::size_t Size = internals::enum_size::value, typename cm::enable_if_t< - std::is_enum::value && + cm::is_scoped_enum::value && std::is_unsigned::type>::value, int> = 0> class enum_set @@ -133,9 +167,16 @@ public: using const_reverse_iterator = std::reverse_iterator; constexpr enum_set() noexcept = default; + enum_set(key_type e) { this->insert(e); } enum_set(enum_set const& other) noexcept { this->insert(other); } enum_set(std::initializer_list list) { this->insert(list); } + enum_set& operator=(key_type e) + { + this->Set.reset(); + this->insert(e); + return *this; + } enum_set& operator=(enum_set const& other) noexcept { this->Set.reset(); @@ -186,41 +227,116 @@ public: size_type max_size() const noexcept { return this->Set.size(); } // Modifiers - void clear() noexcept { this->Set.reset(); } - enum_set& operator+=(key_type e) + // set all elements + enum_set& set() + { + this->Set.set(); + return *this; + } + enum_set& set(key_type e) { this->insert(e); return *this; } - enum_set& operator+=(enum_set const& other) noexcept + enum_set& set(enum_set const& other) noexcept { - this->erase(other); + this->insert(other); return *this; } - enum_set& operator+=(std::initializer_list list) + enum_set& set(std::initializer_list list) { this->insert(list); return *this; } + // alternate syntax for bit set + enum_set& operator+=(key_type e) { return this->set(e); } + enum_set& operator+=(enum_set const& other) noexcept + { + return this->set(other); + } + enum_set& operator+=(std::initializer_list list) + { + return this->set(list); + } + // alternate syntax for bit set + enum_set& operator|=(key_type e) { return this->set(e); } + enum_set& operator|=(enum_set const& other) noexcept + { + return this->set(other); + } + enum_set& operator|=(std::initializer_list list) + { + return this->set(list); + } - enum_set& operator-=(key_type e) + // reset all elements + void clear() noexcept { this->Set.reset(); } + enum_set& reset() + { + this->Set.reset(); + return *this; + } + enum_set& reset(key_type e) { this->erase(e); return *this; } - enum_set& operator-=(enum_set const& other) noexcept + enum_set& reset(enum_set const& other) noexcept { this->erase(other); return *this; } - enum_set& operator-=(std::initializer_list list) + enum_set& reset(std::initializer_list list) { this->erase(list); return *this; } + // alternate syntax for bit reset + enum_set& operator-=(key_type e) { return this->reset(e); } + enum_set& operator-=(enum_set const& other) noexcept + { + return this->reset(other); + } + enum_set& operator-=(std::initializer_list list) + { + return this->reset(list); + } - std::pair insert(value_type value) + // toggle the specified enum + enum_set& flip(key_type e) + { + this->Set.flip(static_cast(e)); + return *this; + } + // toggle all the enums stored in the other enum_set + enum_set& flip(enum_set const& other) noexcept + { + this->Set ^= other.Set; + return *this; + } + // toggle all the enums specified in the list + enum_set& flip(std::initializer_list list) + { + for (auto e : list) { + this->Set.flip(static_cast(e)); + } + return *this; + } + // alternate syntax for bit toggle + enum_set& operator^=(key_type key) { return this->flip(key); } + // toggle all the enums stored in the other enum_set + enum_set& operator^=(enum_set const& other) noexcept + { + return this->flip(other); + } + // toggle all the enums specified in the list + enum_set& operator^=(std::initializer_list list) + { + return this->flip(list); + } + + std::pair insert(key_type value) { auto exist = this->contains(value); if (!exist) { @@ -280,18 +396,6 @@ public: other.Set = tmp; } - // toggle the specified enum - void flip(key_type key) { this->Set.flip(static_cast(key)); } - // toggle all the enums stored in the other enum_set - void flip(enum_set const& other) noexcept { this->Set ^= other.Set; } - // toggle all the enums specified in the list - void flip(std::initializer_list list) - { - for (auto e : list) { - this->Set.flip(static_cast(e)); - } - } - // Lookup size_type count(key_type e) const { return this->contains(e) ? 1 : 0; } @@ -310,11 +414,37 @@ public: return this->end(); } + // Checks bool contains(key_type e) const { return this->Set.test(static_cast(e)); } + bool all() const { return this->Set.all(); } + bool any() const { return this->Set.any(); } + bool none() const { return this->Set.none(); } + // alternate syntax to none() + bool operator!() const { return this->Set.none(); } + + bool all_of(enum_set const& set) const + { + auto result = set; + result.Set &= this->Set; + return result == set; + } + bool any_of(enum_set const& set) const + { + auto result = set; + result.Set &= this->Set; + return result.any(); + } + bool none_of(enum_set const& set) const + { + auto result = set; + result.Set &= this->Set; + return result.none(); + } + private: template friend inline bool operator==(enum_set const& lhs, @@ -328,44 +458,79 @@ private: bool test(size_type pos) const { return this->Set.test(pos); } - std::bitset::digits> Set; + std::bitset Set; }; // non-member functions for enum_set template inline enum_set operator+(enum_set const& lhs, Enum rhs) { - return enum_set(lhs) += rhs; + return enum_set{ lhs } += rhs; } template inline enum_set operator+(enum_set const& lhs, enum_set const& rhs) noexcept { - return enum_set(lhs) += rhs; + return enum_set{ lhs } += rhs; } template inline enum_set operator+(enum_set const& lhs, std::initializer_list const rhs) { - return enum_set(lhs) += rhs; + return enum_set{ lhs } += rhs; +} + +template +inline cm::enum_set operator|(cm::enum_set const& lhs, Enum rhs) +{ + return enum_set{ lhs } |= rhs; +} +template +inline cm::enum_set operator|(Enum lhs, cm::enum_set const& rhs) +{ + return enum_set{ lhs } |= rhs; +} +template +inline cm::enum_set operator|(cm::enum_set const& lhs, + cm::enum_set const& rhs) +{ + return enum_set{ lhs } |= rhs; } template inline enum_set operator-(enum_set const& lhs, Enum rhs) { - return enum_set(lhs) -= rhs; + return enum_set{ lhs } -= rhs; } template inline enum_set operator-(enum_set const& lhs, enum_set const& rhs) noexcept { - return enum_set(lhs) -= rhs; + return enum_set{ lhs } -= rhs; } template inline enum_set operator-(enum_set const& lhs, std::initializer_list const rhs) { - return enum_set(lhs) -= rhs; + return enum_set{ lhs } -= rhs; +} + +template +inline enum_set operator^(enum_set const& lhs, Enum rhs) +{ + return enum_set{ lhs } ^= rhs; +} +template +inline enum_set operator^(enum_set const& lhs, + enum_set const& rhs) noexcept +{ + return enum_set{ lhs } ^= rhs; +} +template +inline enum_set operator^(enum_set const& lhs, + std::initializer_list const rhs) +{ + return enum_set{ lhs } ^= rhs; } template @@ -398,3 +563,17 @@ inline void erase_if(enum_set& set, Predicate pred) } } } // namespace cm + +template ::value, int> = 0> +inline cm::enum_set operator+(Enum lhs, Enum rhs) +{ + return cm::enum_set{ lhs, rhs }; +} +// Alternate syntax +template ::value, int> = 0> +inline cm::enum_set operator|(Enum lhs, Enum rhs) +{ + return cm::enum_set{ lhs, rhs }; +}