| // -*-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 <bitset> |
| #include <cstddef> |
| #include <initializer_list> |
| #include <iterator> |
| #include <limits> |
| #include <utility> |
| |
| #include <cm/type_traits> |
| |
| // |
| // 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<Example, 4>; |
| // |
| // 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 <typename EnumSet> |
| 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<reference>(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<typename std::underlying_type<Enum>::type>::digits, |
| typename cm::enable_if_t< |
| cm::is_scoped_enum<Enum>::value && |
| std::is_unsigned<typename std::underlying_type<Enum>::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<Enum>::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<enum_set>; |
| using const_iterator = enum_set_iterator<enum_set const>; |
| using reverse_iterator = std::reverse_iterator<iterator>; |
| using const_reverse_iterator = std::reverse_iterator<const_iterator>; |
| |
| constexpr enum_set() noexcept {} |
| enum_set(key_type e) { this->insert(e); } |
| enum_set(enum_set const& other) noexcept { this->insert(other); } |
| template <typename E, |
| typename cm::enable_if_t<std::is_same<Enum, E>::value, int> = 0> |
| enum_set(enum_set<E> const& other) noexcept |
| { |
| static_assert(Size < enum_set<E>::set_size, "Incompatible sizes"); |
| |
| this->insert(other.cbegin(), other.cend()); |
| } |
| enum_set(std::initializer_list<value_type> 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<value_type> 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<value_type> 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<value_type> 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<value_type> 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<value_type> 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<value_type> list) |
| { |
| return this->reset(list); |
| } |
| |
| // toggle the specified enum |
| enum_set& flip(key_type e) |
| { |
| this->Set.flip(static_cast<size_type>(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<value_type> list) |
| { |
| for (auto e : list) { |
| this->Set.flip(static_cast<size_type>(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<value_type> list) |
| { |
| return this->flip(list); |
| } |
| |
| std::pair<iterator, bool> insert(key_type value) |
| { |
| auto exist = this->contains(value); |
| if (!exist) { |
| this->Set.set(static_cast<size_type>(value)); |
| } |
| |
| return { iterator(this, static_cast<size_type>(value)), !exist }; |
| } |
| template <typename InputIt> |
| 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<value_type> list) |
| { |
| for (auto e : list) { |
| this->Set.set(static_cast<size_type>(e)); |
| } |
| } |
| |
| size_type erase(key_type key) |
| { |
| if (this->contains(key)) { |
| this->Set.reset(static_cast<size_type>(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<size_type>(*pos)); |
| } |
| void erase(enum_set const& other) noexcept { this->Set &= ~other.Set; } |
| void erase(std::initializer_list<value_type> list) |
| { |
| for (auto e : list) { |
| this->Set.reset(static_cast<size_type>(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<size_type>(e)); |
| } |
| return this->end(); |
| } |
| const_iterator find(key_type e) const |
| { |
| if (this->contains(e)) { |
| return const_iterator(this, static_cast<size_type>(e)); |
| } |
| return this->end(); |
| } |
| |
| // Checks |
| bool contains(key_type e) const |
| { |
| return this->Set.test(static_cast<size_type>(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 <typename E, std::size_t S> |
| friend inline bool operator==(enum_set<E, S> const& lhs, |
| enum_set<E, S> const& rhs) noexcept; |
| |
| template <typename E, std::size_t S, typename Predicate> |
| friend inline void erase_if(enum_set<E, S>& set, Predicate pred); |
| |
| friend class enum_set_iterator<enum_set>; |
| friend class enum_set_iterator<enum_set const>; |
| |
| bool test(size_type pos) const { return this->Set.test(pos); } |
| |
| std::bitset<Size> Set; |
| }; |
| |
| // non-member functions for enum_set |
| template <typename Enum, std::size_t Size> |
| inline enum_set<Enum, Size> operator+(enum_set<Enum, Size> const& lhs, |
| Enum rhs) |
| { |
| return enum_set<Enum, Size>{ lhs } += rhs; |
| } |
| template <typename Enum, std::size_t Size> |
| inline enum_set<Enum, Size> operator+(enum_set<Enum, Size> const& lhs, |
| enum_set<Enum, Size> const& rhs) noexcept |
| { |
| return enum_set<Enum, Size>{ lhs } += rhs; |
| } |
| template <typename Enum, std::size_t Size> |
| inline enum_set<Enum, Size> operator+(enum_set<Enum, Size> const& lhs, |
| std::initializer_list<Enum> const rhs) |
| { |
| return enum_set<Enum, Size>{ lhs } += rhs; |
| } |
| |
| template <typename Enum, std::size_t Size> |
| inline cm::enum_set<Enum, Size> operator|(cm::enum_set<Enum, Size> const& lhs, |
| Enum rhs) |
| { |
| return enum_set<Enum, Size>{ lhs } |= rhs; |
| } |
| template <typename Enum, std::size_t Size> |
| inline cm::enum_set<Enum, Size> operator|(Enum lhs, |
| cm::enum_set<Enum, Size> const& rhs) |
| { |
| return enum_set<Enum, Size>{ lhs } |= rhs; |
| } |
| template <typename Enum, std::size_t Size> |
| inline cm::enum_set<Enum, Size> operator|(cm::enum_set<Enum, Size> const& lhs, |
| cm::enum_set<Enum, Size> const& rhs) |
| { |
| return enum_set<Enum, Size>{ lhs } |= rhs; |
| } |
| |
| template <typename Enum, std::size_t Size> |
| inline enum_set<Enum, Size> operator-(enum_set<Enum, Size> const& lhs, |
| Enum rhs) |
| { |
| return enum_set<Enum, Size>{ lhs } -= rhs; |
| } |
| template <typename Enum, std::size_t Size> |
| inline enum_set<Enum, Size> operator-(enum_set<Enum, Size> const& lhs, |
| enum_set<Enum, Size> const& rhs) noexcept |
| { |
| return enum_set<Enum, Size>{ lhs } -= rhs; |
| } |
| template <typename Enum, std::size_t Size> |
| inline enum_set<Enum, Size> operator-(enum_set<Enum, Size> const& lhs, |
| std::initializer_list<Enum> const rhs) |
| { |
| return enum_set<Enum, Size>{ lhs } -= rhs; |
| } |
| |
| template <typename Enum, std::size_t Size> |
| inline enum_set<Enum, Size> operator^(enum_set<Enum, Size> const& lhs, |
| Enum rhs) |
| { |
| return enum_set<Enum, Size>{ lhs } ^= rhs; |
| } |
| template <typename Enum, std::size_t Size> |
| inline enum_set<Enum, Size> operator^(enum_set<Enum, Size> const& lhs, |
| enum_set<Enum, Size> const& rhs) noexcept |
| { |
| return enum_set<Enum, Size>{ lhs } ^= rhs; |
| } |
| template <typename Enum, std::size_t Size> |
| inline enum_set<Enum, Size> operator^(enum_set<Enum, Size> const& lhs, |
| std::initializer_list<Enum> const rhs) |
| { |
| return enum_set<Enum, Size>{ lhs } ^= rhs; |
| } |
| |
| template <typename Enum, std::size_t Size> |
| inline bool operator==(enum_set<Enum, Size> const& lhs, |
| enum_set<Enum, Size> const& rhs) noexcept |
| { |
| return lhs.Set == rhs.Set; |
| } |
| |
| template <typename Enum, std::size_t Size> |
| inline bool operator!=(enum_set<Enum, Size> const& lhs, |
| enum_set<Enum, Size> const& rhs) noexcept |
| { |
| return !(lhs == rhs); |
| } |
| |
| template <typename Enum, std::size_t Size> |
| inline void erase(enum_set<Enum, Size>& set, Enum value) |
| { |
| set.erase(value); |
| } |
| |
| template <typename Enum, std::size_t Size, typename Predicate> |
| inline void erase_if(enum_set<Enum, Size>& set, Predicate pred) |
| { |
| for (std::size_t index = 0; index < set.Set.size(); ++index) { |
| if (set.Set.test(index) && pred(static_cast<Enum>(index))) { |
| set.Set.reset(index); |
| } |
| } |
| } |
| } // namespace cm |
| |
| // |
| // WARNING: the following two operators rely on the enum_set_traits<Enum> |
| // 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 <typename Enum> |
| struct cm_enum_set_traits |
| { |
| }; |
| |
| namespace cm { |
| template <typename Enum, typename = cm::void_t<>> |
| struct is_enum_set : std::false_type |
| { |
| }; |
| template <typename Enum> |
| struct is_enum_set<Enum, cm::void_t<typename cm_enum_set_traits<Enum>::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<E::value_type> \ |
| { \ |
| 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<E::value_type> \ |
| { \ |
| using type = E; \ |
| using value_type = E::value_type; \ |
| }; |
| |
| template <typename Enum, |
| typename cm::enable_if_t<cm::is_enum_set<Enum>::value, int> = 0> |
| inline typename cm_enum_set_traits<Enum>::type operator+(Enum lhs, Enum rhs) |
| { |
| return { lhs, rhs }; |
| } |
| // Alternate syntax |
| template <typename Enum, |
| typename cm::enable_if_t<cm::is_enum_set<Enum>::value, int> = 0> |
| inline typename cm_enum_set_traits<Enum>::type operator|(Enum lhs, Enum rhs) |
| { |
| return { lhs, rhs }; |
| } |
| #endif |