From b13eef5a909ba038e71cf5e788930bceb4ca3e0d Mon Sep 17 00:00:00 2001 From: Marc Chevrier Date: Wed, 27 Apr 2022 19:24:57 +0200 Subject: cm::enum_set: container that contains a set of unique enum values. The enum must be an `enum class` with an unsigned integer as base type. --- Utilities/std/cmext/enum_set | 393 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 393 insertions(+) create mode 100644 Utilities/std/cmext/enum_set (limited to 'Utilities/std') diff --git a/Utilities/std/cmext/enum_set b/Utilities/std/cmext/enum_set new file mode 100644 index 0000000000..f97a04c841 --- /dev/null +++ b/Utilities/std/cmext/enum_set @@ -0,0 +1,393 @@ +// -*-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 +#include +#include +#include +#include +#include + +#include + +// +// 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 +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--() + { + while (--this->Index >= 0 && !this->Set->test(this->Index)) + ; + + 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, + typename cm::enable_if_t< + std::is_enum::value && + std::is_unsigned::type>::value, + int> = 0> +class enum_set +{ +public: + 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 = const Enum*; + using const_pointer = const Enum*; + + 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(const enum_set& other) noexcept { this->insert(other); } + enum_set(std::initializer_list 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 list) + { + this->Set.reset(); + this->insert(list); + } + + // 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 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 list) + { + this->erase(list); + return *this; + } + + std::pair insert(value_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(const enum_set& 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(const enum_set& 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; + } + + // 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(const enum_set& 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; } + + iterator find(key_type e) + { + if (this->contains(e)) { + return iterator(this, static_cast(e)); + } else { + return this->end(); + } + } + const_iterator find(key_type e) const + { + if (this->contains(e)) { + return const_iterator(this, static_cast(e)); + } else { + return this->end(); + } + } + + bool contains(key_type e) const + { + return this->Set.test(static_cast(e)); + } + +private: + 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::digits> Set; +}; + +// non-member functions for enum_set +template +inline enum_set operator+(const enum_set& lhs, Enum rhs) +{ + return enum_set(lhs) += rhs; +} +template +inline enum_set operator+(const enum_set& lhs, + const enum_set& rhs) noexcept +{ + return enum_set(lhs) += rhs; +} +template +inline enum_set operator+(const enum_set& lhs, + const std::initializer_list rhs) +{ + return enum_set(lhs) += rhs; +} + +template +inline enum_set operator-(const enum_set& lhs, Enum rhs) +{ + return enum_set(lhs) -= rhs; +} +template +inline enum_set operator-(const enum_set& lhs, + const enum_set& rhs) noexcept +{ + return enum_set(lhs) -= rhs; +} +template +inline enum_set operator-(const enum_set& lhs, + const std::initializer_list rhs) +{ + return enum_set(lhs) -= rhs; +} + +template +inline bool operator==(const enum_set& lhs, + const enum_set& rhs) noexcept +{ + return lhs == rhs; +} + +template +inline bool operator!=(const enum_set& lhs, + const enum_set& 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 -- cgit v1.2.1