enum_set 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. // -*-c++-*-
  2. // vim: set ft=cpp:
  3. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  4. file Copyright.txt or https://cmake.org/licensing for details. */
  5. #pragma once
  6. #include <bitset>
  7. #include <cstddef>
  8. #include <initializer_list>
  9. #include <iterator>
  10. #include <limits>
  11. #include <utility>
  12. #include <cm/type_traits>
  13. //
  14. // Class enum_set offers the capability to manage a set of enum values
  15. // Only 'enum class' with unsigned base type are supported.
  16. //
  17. // The methods offered by 'enum_set' are close as possible to the 'std::set'
  18. // container plus some useful methods from 'std::bitset' like 'flip'.
  19. //
  20. // Internally, this class use 'std::bitset' container to manage the
  21. // set of enum. The size of the bitset is deduced from the underlying type of
  22. // the enum.
  23. //
  24. namespace cm {
  25. template <typename EnumSet>
  26. class enum_set_iterator
  27. {
  28. public:
  29. enum_set_iterator() = default;
  30. enum_set_iterator(const enum_set_iterator& other) = default;
  31. using iterator_category = std::bidirectional_iterator_tag;
  32. using value_type = typename EnumSet::value_type;
  33. using difference_type = typename EnumSet::difference_type;
  34. using reference = typename EnumSet::reference;
  35. using pointer = typename EnumSet::pointer;
  36. enum_set_iterator& operator++()
  37. {
  38. while (++this->Index < this->Set->max_size() &&
  39. !this->Set->test(this->Index))
  40. ;
  41. return *this;
  42. }
  43. enum_set_iterator operator++(int)
  44. {
  45. auto retval = *this;
  46. ++(*this);
  47. return retval;
  48. }
  49. enum_set_iterator& operator--()
  50. {
  51. if (this->Index == 0) {
  52. return *this;
  53. }
  54. while (!this->Set->test(--this->Index) && this->Index != 0)
  55. ;
  56. return *this;
  57. }
  58. enum_set_iterator operator--(int)
  59. {
  60. auto retval = *this;
  61. --(*this);
  62. return retval;
  63. }
  64. reference operator*() const { return static_cast<reference>(this->Index); }
  65. bool operator==(enum_set_iterator other) const
  66. {
  67. return (this->Set == other.Set) && (this->Index == other.Index);
  68. }
  69. bool operator!=(enum_set_iterator other) const { return !(*this == other); }
  70. private:
  71. friend EnumSet;
  72. using size_type = typename EnumSet::size_type;
  73. enum_set_iterator(EnumSet* set, bool at_end = false)
  74. : Set(set)
  75. {
  76. if (at_end || this->Set->empty()) {
  77. this->Index = this->Set->max_size();
  78. } else {
  79. while (!this->Set->test(this->Index) &&
  80. ++this->Index < this->Set->max_size())
  81. ;
  82. }
  83. }
  84. enum_set_iterator(EnumSet* set, size_type pos)
  85. : Index(pos)
  86. , Set(set)
  87. {
  88. }
  89. std::size_t Index = 0;
  90. EnumSet* Set = nullptr;
  91. };
  92. template <
  93. typename Enum,
  94. typename cm::enable_if_t<
  95. std::is_enum<Enum>::value &&
  96. std::is_unsigned<typename std::underlying_type<Enum>::type>::value,
  97. int> = 0>
  98. class enum_set
  99. {
  100. public:
  101. using key_type = Enum;
  102. using value_type = Enum;
  103. using size_type = typename std::underlying_type<Enum>::type;
  104. using difference_type = size_type;
  105. using reference = Enum;
  106. using const_reference = Enum;
  107. using pointer = const Enum*;
  108. using const_pointer = const Enum*;
  109. using iterator = enum_set_iterator<enum_set>;
  110. using const_iterator = enum_set_iterator<const enum_set>;
  111. using reverse_iterator = std::reverse_iterator<iterator>;
  112. using const_reverse_iterator = std::reverse_iterator<const_iterator>;
  113. constexpr enum_set() noexcept = default;
  114. enum_set(const enum_set& other) noexcept { this->insert(other); }
  115. enum_set(std::initializer_list<value_type> list) { this->insert(list); }
  116. enum_set& operator=(const enum_set& other) noexcept
  117. {
  118. this->Set.reset();
  119. this->Set |= other.Set;
  120. return *this;
  121. }
  122. enum_set& operator=(std::initializer_list<value_type> list)
  123. {
  124. this->Set.reset();
  125. this->insert(list);
  126. }
  127. // Iterators
  128. iterator begin() noexcept { return iterator(this); }
  129. const_iterator begin() const noexcept { return const_iterator(this); }
  130. const_iterator cbegin() const noexcept { return const_iterator(this); }
  131. iterator end() noexcept { return iterator(this, true); }
  132. const_iterator end() const noexcept { return const_iterator(this, true); }
  133. const_iterator cend() const noexcept { return const_iterator(this, true); }
  134. reverse_iterator rbegin() noexcept { return reverse_iterator(this->end()); }
  135. const_reverse_iterator rbegin() const noexcept
  136. {
  137. return const_reverse_iterator(this->end());
  138. }
  139. const_reverse_iterator crbegin() const noexcept
  140. {
  141. return const_reverse_iterator(this->cend());
  142. }
  143. reverse_iterator rend() noexcept { return reverse_iterator(this->begin()); }
  144. const_reverse_iterator rend() const noexcept
  145. {
  146. return const_reverse_iterator(this->begin());
  147. }
  148. const_reverse_iterator crend() const noexcept
  149. {
  150. return const_reverse_iterator(this->cbegin());
  151. }
  152. // Capacity
  153. bool empty() const noexcept { return this->Set.none(); }
  154. size_type size() const noexcept { return this->Set.count(); }
  155. size_type max_size() const noexcept { return this->Set.size(); }
  156. // Modifiers
  157. void clear() noexcept { this->Set.reset(); }
  158. enum_set& operator+=(key_type e)
  159. {
  160. this->insert(e);
  161. return *this;
  162. }
  163. enum_set& operator+=(const enum_set& other) noexcept
  164. {
  165. this->erase(other);
  166. return *this;
  167. }
  168. enum_set& operator+=(std::initializer_list<value_type> list)
  169. {
  170. this->insert(list);
  171. return *this;
  172. }
  173. enum_set& operator-=(key_type e)
  174. {
  175. this->erase(e);
  176. return *this;
  177. }
  178. enum_set& operator-=(const enum_set& other) noexcept
  179. {
  180. this->erase(other);
  181. return *this;
  182. }
  183. enum_set& operator-=(std::initializer_list<value_type> list)
  184. {
  185. this->erase(list);
  186. return *this;
  187. }
  188. std::pair<iterator, bool> insert(value_type value)
  189. {
  190. auto exist = this->contains(value);
  191. if (!exist) {
  192. this->Set.set(static_cast<size_type>(value));
  193. }
  194. return { iterator(this, static_cast<size_type>(value)), !exist };
  195. }
  196. template <typename InputIt>
  197. void insert(InputIt first, InputIt last)
  198. {
  199. for (auto i = first; i != last; i++) {
  200. this->insert(*i);
  201. }
  202. }
  203. void insert(const enum_set& other) noexcept { this->Set |= other.Set; }
  204. void insert(std::initializer_list<value_type> list)
  205. {
  206. for (auto e : list) {
  207. this->Set.set(static_cast<size_type>(e));
  208. }
  209. }
  210. size_type erase(key_type key)
  211. {
  212. if (this->contains(key)) {
  213. this->Set.reset(static_cast<size_type>(key));
  214. return 1;
  215. }
  216. return 0;
  217. }
  218. iterator erase(iterator pos)
  219. {
  220. this->erase(*pos++);
  221. return pos;
  222. }
  223. iterator erase(const_iterator pos)
  224. {
  225. this->erase(*pos++);
  226. return pos == this->cend() ? this->end()
  227. : iterator(this, static_cast<size_type>(*pos));
  228. }
  229. void erase(const enum_set& other) noexcept { this->Set &= ~other.Set; }
  230. void erase(std::initializer_list<value_type> list)
  231. {
  232. for (auto e : list) {
  233. this->Set.reset(static_cast<size_type>(e));
  234. }
  235. }
  236. void swap(enum_set& other) noexcept
  237. {
  238. auto tmp = this->Set;
  239. this->Set = other.Set;
  240. other.Set = tmp;
  241. }
  242. // toggle the specified enum
  243. void flip(key_type key) { this->Set.flip(static_cast<size_type>(key)); }
  244. // toggle all the enums stored in the other enum_set
  245. void flip(const enum_set& other) noexcept { this->Set ^= other.Set; }
  246. // toggle all the enums specified in the list
  247. void flip(std::initializer_list<value_type> list)
  248. {
  249. for (auto e : list) {
  250. this->Set.flip(static_cast<size_type>(e));
  251. }
  252. }
  253. // Lookup
  254. size_type count(key_type e) const { return this->contains(e) ? 1 : 0; }
  255. iterator find(key_type e)
  256. {
  257. if (this->contains(e)) {
  258. return iterator(this, static_cast<size_type>(e));
  259. } else {
  260. return this->end();
  261. }
  262. }
  263. const_iterator find(key_type e) const
  264. {
  265. if (this->contains(e)) {
  266. return const_iterator(this, static_cast<size_type>(e));
  267. } else {
  268. return this->end();
  269. }
  270. }
  271. bool contains(key_type e) const
  272. {
  273. return this->Set.test(static_cast<size_type>(e));
  274. }
  275. private:
  276. template <typename E, typename Predicate>
  277. friend inline void erase_if(enum_set<E>& set, Predicate pred);
  278. friend class enum_set_iterator<enum_set>;
  279. friend class enum_set_iterator<const enum_set>;
  280. bool test(size_type pos) const { return this->Set.test(pos); }
  281. std::bitset<std::numeric_limits<size_type>::digits> Set;
  282. };
  283. // non-member functions for enum_set
  284. template <typename Enum>
  285. inline enum_set<Enum> operator+(const enum_set<Enum>& lhs, Enum rhs)
  286. {
  287. return enum_set<Enum>(lhs) += rhs;
  288. }
  289. template <typename Enum>
  290. inline enum_set<Enum> operator+(const enum_set<Enum>& lhs,
  291. const enum_set<Enum>& rhs) noexcept
  292. {
  293. return enum_set<Enum>(lhs) += rhs;
  294. }
  295. template <typename Enum>
  296. inline enum_set<Enum> operator+(const enum_set<Enum>& lhs,
  297. const std::initializer_list<Enum> rhs)
  298. {
  299. return enum_set<Enum>(lhs) += rhs;
  300. }
  301. template <typename Enum>
  302. inline enum_set<Enum> operator-(const enum_set<Enum>& lhs, Enum rhs)
  303. {
  304. return enum_set<Enum>(lhs) -= rhs;
  305. }
  306. template <typename Enum>
  307. inline enum_set<Enum> operator-(const enum_set<Enum>& lhs,
  308. const enum_set<Enum>& rhs) noexcept
  309. {
  310. return enum_set<Enum>(lhs) -= rhs;
  311. }
  312. template <typename Enum>
  313. inline enum_set<Enum> operator-(const enum_set<Enum>& lhs,
  314. const std::initializer_list<Enum> rhs)
  315. {
  316. return enum_set<Enum>(lhs) -= rhs;
  317. }
  318. template <typename Enum>
  319. inline bool operator==(const enum_set<Enum>& lhs,
  320. const enum_set<Enum>& rhs) noexcept
  321. {
  322. return lhs == rhs;
  323. }
  324. template <typename Enum>
  325. inline bool operator!=(const enum_set<Enum>& lhs,
  326. const enum_set<Enum>& rhs) noexcept
  327. {
  328. return !(lhs == rhs);
  329. }
  330. template <typename Enum>
  331. inline void erase(enum_set<Enum>& set, Enum value)
  332. {
  333. set.erase(value);
  334. }
  335. template <typename Enum, typename Predicate>
  336. inline void erase_if(enum_set<Enum>& set, Predicate pred)
  337. {
  338. for (std::size_t index = 0; index < set.Set.size(); ++index) {
  339. if (set.Set.test(index) && pred(static_cast<Enum>(index))) {
  340. set.Set.reset(index);
  341. }
  342. }
  343. }
  344. } // namespace cm