| // -*-c++-*- | |
| // vim: set ft=cpp: | |
| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file Copyright.txt 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 'enum class' with unsigned base type are supported. | |
| // | |
| // The methods offered by 'enum_set' are close as possible to the 'std::set' | |
| // container plus some useful methods from 'std::bitset' like 'flip'. | |
| // | |
| // 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. | |
| // | |
| namespace cm { | |
| template <typename EnumSet> | |
| class enum_set_iterator | |
| { | |
| public: | |
| enum_set_iterator() = default; | |
| enum_set_iterator(const enum_set_iterator& 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, | |
| typename cm::enable_if_t< | |
| std::is_enum<Enum>::value && | |
| std::is_unsigned<typename std::underlying_type<Enum>::type>::value, | |
| int> = 0> | |
| class enum_set | |
| { | |
| public: | |
| 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 = const Enum*; | |
| using const_pointer = const Enum*; | |
| using iterator = enum_set_iterator<enum_set>; | |
| using const_iterator = enum_set_iterator<const enum_set>; | |
| using reverse_iterator = std::reverse_iterator<iterator>; | |
| using const_reverse_iterator = std::reverse_iterator<const_iterator>; | |
| constexpr enum_set() noexcept = default; | |
| enum_set(const enum_set& other) noexcept { this->insert(other); } | |
| enum_set(std::initializer_list<value_type> list) { this->insert(list); } | |
| enum_set& operator=(const enum_set& 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 | |
| void clear() noexcept { this->Set.reset(); } | |
| enum_set& operator+=(key_type e) | |
| { | |
| this->insert(e); | |
| return *this; | |
| } | |
| enum_set& operator+=(const enum_set& other) noexcept | |
| { | |
| this->erase(other); | |
| return *this; | |
| } | |
| enum_set& operator+=(std::initializer_list<value_type> list) | |
| { | |
| this->insert(list); | |
| return *this; | |
| } | |
| enum_set& operator-=(key_type e) | |
| { | |
| this->erase(e); | |
| return *this; | |
| } | |
| enum_set& operator-=(const enum_set& other) noexcept | |
| { | |
| this->erase(other); | |
| return *this; | |
| } | |
| enum_set& operator-=(std::initializer_list<value_type> list) | |
| { | |
| this->erase(list); | |
| return *this; | |
| } | |
| std::pair<iterator, bool> insert(value_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(const enum_set& 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(const enum_set& 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; | |
| } | |
| // toggle the specified enum | |
| void flip(key_type key) { this->Set.flip(static_cast<size_type>(key)); } | |
| // toggle all the enums stored in the other enum_set | |
| void flip(const enum_set& other) noexcept { this->Set ^= other.Set; } | |
| // toggle all the enums specified in the list | |
| void flip(std::initializer_list<value_type> list) | |
| { | |
| for (auto e : list) { | |
| this->Set.flip(static_cast<size_type>(e)); | |
| } | |
| } | |
| // 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(); | |
| } | |
| bool contains(key_type e) const | |
| { | |
| return this->Set.test(static_cast<size_type>(e)); | |
| } | |
| private: | |
| template <typename E> | |
| friend inline bool operator==(const enum_set<E>& lhs, | |
| const enum_set<E>& rhs) noexcept; | |
| template <typename E, typename Predicate> | |
| friend inline void erase_if(enum_set<E>& set, Predicate pred); | |
| friend class enum_set_iterator<enum_set>; | |
| friend class enum_set_iterator<const enum_set>; | |
| bool test(size_type pos) const { return this->Set.test(pos); } | |
| std::bitset<std::numeric_limits<size_type>::digits> Set; | |
| }; | |
| // non-member functions for enum_set | |
| template <typename Enum> | |
| inline enum_set<Enum> operator+(const enum_set<Enum>& lhs, Enum rhs) | |
| { | |
| return enum_set<Enum>(lhs) += rhs; | |
| } | |
| template <typename Enum> | |
| inline enum_set<Enum> operator+(const enum_set<Enum>& lhs, | |
| const enum_set<Enum>& rhs) noexcept | |
| { | |
| return enum_set<Enum>(lhs) += rhs; | |
| } | |
| template <typename Enum> | |
| inline enum_set<Enum> operator+(const enum_set<Enum>& lhs, | |
| const std::initializer_list<Enum> rhs) | |
| { | |
| return enum_set<Enum>(lhs) += rhs; | |
| } | |
| template <typename Enum> | |
| inline enum_set<Enum> operator-(const enum_set<Enum>& lhs, Enum rhs) | |
| { | |
| return enum_set<Enum>(lhs) -= rhs; | |
| } | |
| template <typename Enum> | |
| inline enum_set<Enum> operator-(const enum_set<Enum>& lhs, | |
| const enum_set<Enum>& rhs) noexcept | |
| { | |
| return enum_set<Enum>(lhs) -= rhs; | |
| } | |
| template <typename Enum> | |
| inline enum_set<Enum> operator-(const enum_set<Enum>& lhs, | |
| const std::initializer_list<Enum> rhs) | |
| { | |
| return enum_set<Enum>(lhs) -= rhs; | |
| } | |
| template <typename Enum> | |
| inline bool operator==(const enum_set<Enum>& lhs, | |
| const enum_set<Enum>& rhs) noexcept | |
| { | |
| return lhs.Set == rhs.Set; | |
| } | |
| template <typename Enum> | |
| inline bool operator!=(const enum_set<Enum>& lhs, | |
| const enum_set<Enum>& rhs) noexcept | |
| { | |
| return !(lhs == rhs); | |
| } | |
| template <typename Enum> | |
| inline void erase(enum_set<Enum>& set, Enum value) | |
| { | |
| set.erase(value); | |
| } | |
| template <typename Enum, typename Predicate> | |
| inline void erase_if(enum_set<Enum>& 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 |