// -*-c++-*- // vim: set ft=cpp: /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file LICENSE.rst or https://cmake.org/licensing for details. */ #pragma once #include #include #include #include #include #include #include // // 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 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 or can be set explicitly as template parameter: // // enum class Example : unsigned { A, B, C, 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 { template class enum_set_iterator { public: enum_set_iterator() = default; enum_set_iterator(enum_set_iterator const& other) = default; using iterator_category = std::bidirectional_iterator_tag; using value_type = typename EnumSet::value_type; using difference_type = typename EnumSet::difference_type; using reference = typename EnumSet::reference; using pointer = typename EnumSet::pointer; enum_set_iterator& operator++() { while (++this->Index < this->Set->max_size() && !this->Set->test(this->Index)) ; return *this; } enum_set_iterator operator++(int) { auto retval = *this; ++(*this); return retval; } enum_set_iterator& operator--() { if (this->Index == 0) { return *this; } while (!this->Set->test(--this->Index) && this->Index != 0) ; return *this; } enum_set_iterator operator--(int) { auto retval = *this; --(*this); return retval; } reference operator*() const { return static_cast(this->Index); } bool operator==(enum_set_iterator other) const { return (this->Set == other.Set) && (this->Index == other.Index); } bool operator!=(enum_set_iterator other) const { return !(*this == other); } private: friend EnumSet; using size_type = typename EnumSet::size_type; enum_set_iterator(EnumSet* set, bool at_end = false) : Set(set) { if (at_end || this->Set->empty()) { this->Index = this->Set->max_size(); } else { while (!this->Set->test(this->Index) && ++this->Index < this->Set->max_size()) ; } } enum_set_iterator(EnumSet* set, size_type pos) : Index(pos) , Set(set) { } std::size_t Index = 0; EnumSet* Set = nullptr; }; template < typename Enum, std::size_t Size = std::numeric_limits::type>::digits, typename cm::enable_if_t< cm::is_scoped_enum::value && std::is_unsigned::type>::value, int> = 0> class enum_set { public: static constexpr std::size_t set_size = Size; using key_type = Enum; using value_type = Enum; using size_type = typename std::underlying_type::type; using difference_type = size_type; using reference = Enum; using const_reference = Enum; using pointer = Enum const*; using const_pointer = Enum const*; using iterator = enum_set_iterator; using const_iterator = enum_set_iterator; using reverse_iterator = std::reverse_iterator; 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); } template ::value, int> = 0> enum_set(enum_set const& other) noexcept { static_assert(Size < enum_set::set_size, "Incompatible sizes"); this->insert(other.cbegin(), other.cend()); } 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(); this->Set |= other.Set; return *this; } enum_set& operator=(std::initializer_list list) { this->Set.reset(); this->insert(list); return *this; } // Iterators iterator begin() noexcept { return iterator(this); } const_iterator begin() const noexcept { return const_iterator(this); } const_iterator cbegin() const noexcept { return const_iterator(this); } iterator end() noexcept { return iterator(this, true); } const_iterator end() const noexcept { return const_iterator(this, true); } const_iterator cend() const noexcept { return const_iterator(this, true); } reverse_iterator rbegin() noexcept { return reverse_iterator(this->end()); } const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(this->end()); } const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(this->cend()); } reverse_iterator rend() noexcept { return reverse_iterator(this->begin()); } const_reverse_iterator rend() const noexcept { return const_reverse_iterator(this->begin()); } const_reverse_iterator crend() const noexcept { return const_reverse_iterator(this->cbegin()); } // Capacity bool empty() const noexcept { return this->Set.none(); } size_type size() const noexcept { return this->Set.count(); } size_type max_size() const noexcept { return this->Set.size(); } // Modifiers // set all elements enum_set& set() { this->Set.set(); return *this; } enum_set& set(key_type e) { this->insert(e); return *this; } enum_set& set(enum_set const& other) noexcept { this->insert(other); return *this; } 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); } // 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& reset(enum_set const& other) noexcept { this->erase(other); return *this; } 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); } // 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) { this->Set.set(static_cast(value)); } return { iterator(this, static_cast(value)), !exist }; } template void insert(InputIt first, InputIt last) { for (auto i = first; i != last; i++) { this->insert(*i); } } void insert(enum_set const& other) noexcept { this->Set |= other.Set; } void insert(std::initializer_list list) { for (auto e : list) { this->Set.set(static_cast(e)); } } size_type erase(key_type key) { if (this->contains(key)) { this->Set.reset(static_cast(key)); return 1; } return 0; } iterator erase(iterator pos) { this->erase(*pos++); return pos; } iterator erase(const_iterator pos) { this->erase(*pos++); return pos == this->cend() ? this->end() : iterator(this, static_cast(*pos)); } void erase(enum_set const& other) noexcept { this->Set &= ~other.Set; } void erase(std::initializer_list list) { for (auto e : list) { this->Set.reset(static_cast(e)); } } void swap(enum_set& other) noexcept { auto tmp = this->Set; this->Set = other.Set; other.Set = tmp; } // Lookup size_type count(key_type e) const { return this->contains(e) ? 1 : 0; } iterator find(key_type e) { if (this->contains(e)) { return iterator(this, static_cast(e)); } return this->end(); } const_iterator find(key_type e) const { if (this->contains(e)) { return const_iterator(this, static_cast(e)); } 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, enum_set const& rhs) noexcept; template friend inline void erase_if(enum_set& set, Predicate pred); friend class enum_set_iterator; friend class enum_set_iterator; bool test(size_type pos) const { return this->Set.test(pos); } 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; } 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 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; } 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 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 inline bool operator==(enum_set const& lhs, enum_set const& rhs) noexcept { return lhs.Set == rhs.Set; } template inline bool operator!=(enum_set const& lhs, enum_set const& rhs) noexcept { return !(lhs == rhs); } template inline void erase(enum_set& set, Enum value) { set.erase(value); } template inline void erase_if(enum_set& set, Predicate pred) { for (std::size_t index = 0; index < set.Set.size(); ++index) { if (set.Set.test(index) && pred(static_cast(index))) { set.Set.reset(index); } } } } // namespace cm // // 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. // // 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 struct cm_enum_set_traits { }; 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 typename cm_enum_set_traits::type operator|(Enum lhs, Enum rhs) { return { lhs, rhs }; } #endif