mirror of
https://github.com/Kitware/CMake.git
synced 2026-05-25 01:28:50 -05:00
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.
This commit is contained in:
@@ -117,6 +117,11 @@ These are:
|
||||
* ``cm::contains``:
|
||||
Checks if element or key is contained in container.
|
||||
|
||||
* ``<cmext/enum_set>``
|
||||
|
||||
* ``cm::enum_set``:
|
||||
Container to manage set of elements from an ``enum class`` definition.
|
||||
|
||||
* ``<cmext/iterator>``:
|
||||
|
||||
* ``cm::is_terator``:
|
||||
|
||||
@@ -29,6 +29,7 @@ set(CMakeLib_TESTS
|
||||
testUVStreambuf.cxx
|
||||
testCMExtMemory.cxx
|
||||
testCMExtAlgorithm.cxx
|
||||
testCMExtEnumSet.cxx
|
||||
)
|
||||
if (CMake_TEST_FILESYSTEM_PATH OR NOT CMake_HAVE_CXX_FILESYSTEM)
|
||||
list(APPEND CMakeLib_TESTS testCMFilesystemPath.cxx)
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
#include <cmext/enum_set>
|
||||
|
||||
namespace {
|
||||
|
||||
int failed = 0;
|
||||
|
||||
void testDeclaration()
|
||||
{
|
||||
std::cout << "testDeclaration()" << std::endl;
|
||||
|
||||
enum class Test : std::uint8_t
|
||||
{
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
D
|
||||
};
|
||||
cm::enum_set<Test> testSet1;
|
||||
cm::enum_set<Test> testSet2{ Test::A, Test::C };
|
||||
cm::enum_set<Test> testSet3 = testSet2;
|
||||
|
||||
if (!testSet1.empty()) {
|
||||
++failed;
|
||||
}
|
||||
if (testSet2.size() != 2) {
|
||||
++failed;
|
||||
}
|
||||
if (testSet3.size() != 2) {
|
||||
++failed;
|
||||
}
|
||||
}
|
||||
|
||||
void testIteration()
|
||||
{
|
||||
std::cout << "testIteration()" << std::endl;
|
||||
|
||||
enum class Test : std::uint8_t
|
||||
{
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
D
|
||||
};
|
||||
cm::enum_set<Test> testSet{ Test::A, Test::C, Test::B };
|
||||
|
||||
if (testSet.size() != 3) {
|
||||
++failed;
|
||||
}
|
||||
|
||||
std::set<std::uint8_t> reference{ static_cast<std::uint8_t>(Test::A),
|
||||
static_cast<std::uint8_t>(Test::B),
|
||||
static_cast<std::uint8_t>(Test::C) };
|
||||
std::set<std::uint8_t> s;
|
||||
|
||||
for (auto e : testSet) {
|
||||
s.insert(static_cast<std::uint8_t>(e));
|
||||
}
|
||||
if (s != reference) {
|
||||
++failed;
|
||||
}
|
||||
|
||||
s.clear();
|
||||
for (auto rit = testSet.rbegin(); rit != testSet.rend(); rit++) {
|
||||
s.insert(static_cast<std::uint8_t>(*rit));
|
||||
}
|
||||
if (s != reference) {
|
||||
++failed;
|
||||
}
|
||||
}
|
||||
|
||||
void testEdition()
|
||||
{
|
||||
std::cout << "testEdition()" << std::endl;
|
||||
|
||||
enum class Test : std::uint8_t
|
||||
{
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
D,
|
||||
E
|
||||
};
|
||||
|
||||
{
|
||||
cm::enum_set<Test> testSet{ Test::A, Test::C, Test::B };
|
||||
|
||||
auto pos = testSet.insert(Test::E);
|
||||
if (!pos.second || testSet.size() != 4 || *(pos.first) != Test::E ||
|
||||
testSet.find(Test::E) == testSet.end()) {
|
||||
++failed;
|
||||
}
|
||||
testSet.insert(Test::E);
|
||||
if (testSet.size() != 4 || testSet.find(Test::E) == testSet.end()) {
|
||||
++failed;
|
||||
}
|
||||
|
||||
testSet.erase(Test::A);
|
||||
if (testSet.size() != 3 || testSet.find(Test::A) != testSet.end()) {
|
||||
++failed;
|
||||
}
|
||||
testSet.erase(Test::A);
|
||||
if (testSet.size() != 3 || testSet.find(Test::A) != testSet.end()) {
|
||||
++failed;
|
||||
}
|
||||
}
|
||||
{
|
||||
cm::enum_set<Test> testSet{ Test::A, Test::C, Test::B };
|
||||
|
||||
testSet += { Test::D, Test::E };
|
||||
|
||||
std::set<std::uint8_t> reference{ static_cast<std::uint8_t>(Test::A),
|
||||
static_cast<std::uint8_t>(Test::B),
|
||||
static_cast<std::uint8_t>(Test::C),
|
||||
static_cast<std::uint8_t>(Test::D),
|
||||
static_cast<std::uint8_t>(Test::E) };
|
||||
std::set<std::uint8_t> s;
|
||||
for (auto e : testSet) {
|
||||
s.insert(static_cast<std::uint8_t>(e));
|
||||
}
|
||||
if (s != reference) {
|
||||
++failed;
|
||||
}
|
||||
|
||||
testSet -= { Test::D, Test::B };
|
||||
reference.erase(static_cast<std::uint8_t>(Test::D));
|
||||
reference.erase(static_cast<std::uint8_t>(Test::B));
|
||||
s.clear();
|
||||
for (auto e : testSet) {
|
||||
s.insert(static_cast<std::uint8_t>(e));
|
||||
}
|
||||
if (s != reference) {
|
||||
++failed;
|
||||
}
|
||||
}
|
||||
{
|
||||
cm::enum_set<Test> testSet1{ Test::A, Test::C, Test::B };
|
||||
cm::enum_set<Test> testSet2{ Test::A, Test::D, Test::E };
|
||||
testSet1.insert(testSet2.cbegin(), testSet2.cend());
|
||||
|
||||
std::set<std::uint8_t> reference{ static_cast<std::uint8_t>(Test::A),
|
||||
static_cast<std::uint8_t>(Test::B),
|
||||
static_cast<std::uint8_t>(Test::C),
|
||||
static_cast<std::uint8_t>(Test::D),
|
||||
static_cast<std::uint8_t>(Test::E) };
|
||||
std::set<std::uint8_t> s;
|
||||
for (auto e : testSet1) {
|
||||
s.insert(static_cast<std::uint8_t>(e));
|
||||
}
|
||||
if (s != reference) {
|
||||
++failed;
|
||||
}
|
||||
|
||||
testSet1.erase(testSet2);
|
||||
|
||||
reference.erase(static_cast<std::uint8_t>(Test::A));
|
||||
reference.erase(static_cast<std::uint8_t>(Test::D));
|
||||
reference.erase(static_cast<std::uint8_t>(Test::E));
|
||||
s.clear();
|
||||
for (auto e : testSet1) {
|
||||
s.insert(static_cast<std::uint8_t>(e));
|
||||
}
|
||||
if (s != reference) {
|
||||
++failed;
|
||||
}
|
||||
}
|
||||
{
|
||||
cm::enum_set<Test> testSet1{ Test::A, Test::C, Test::B };
|
||||
cm::enum_set<Test> testSet2{ Test::C, Test::E };
|
||||
|
||||
testSet1.flip(Test::A);
|
||||
if (testSet1.size() != 2 || testSet1.contains(Test::A)) {
|
||||
++failed;
|
||||
}
|
||||
|
||||
testSet1.flip(testSet2);
|
||||
std::set<std::uint8_t> reference{ static_cast<std::uint8_t>(Test::B),
|
||||
static_cast<std::uint8_t>(Test::E) };
|
||||
std::set<std::uint8_t> s;
|
||||
for (auto e : testSet1) {
|
||||
s.insert(static_cast<std::uint8_t>(e));
|
||||
}
|
||||
if (s != reference) {
|
||||
++failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int testCMExtEnumSet(int /*unused*/, char* /*unused*/ [])
|
||||
{
|
||||
testDeclaration();
|
||||
testIteration();
|
||||
testEdition();
|
||||
|
||||
return failed;
|
||||
}
|
||||
@@ -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 <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--()
|
||||
{
|
||||
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<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);
|
||||
}
|
||||
|
||||
// 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));
|
||||
} else {
|
||||
return this->end();
|
||||
}
|
||||
}
|
||||
const_iterator find(key_type e) const
|
||||
{
|
||||
if (this->contains(e)) {
|
||||
return const_iterator(this, static_cast<size_type>(e));
|
||||
} else {
|
||||
return this->end();
|
||||
}
|
||||
}
|
||||
|
||||
bool contains(key_type e) const
|
||||
{
|
||||
return this->Set.test(static_cast<size_type>(e));
|
||||
}
|
||||
|
||||
private:
|
||||
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 == rhs;
|
||||
}
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user