enum_set 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  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. return *this;
  127. }
  128. // Iterators
  129. iterator begin() noexcept { return iterator(this); }
  130. const_iterator begin() const noexcept { return const_iterator(this); }
  131. const_iterator cbegin() const noexcept { return const_iterator(this); }
  132. iterator end() noexcept { return iterator(this, true); }
  133. const_iterator end() const noexcept { return const_iterator(this, true); }
  134. const_iterator cend() const noexcept { return const_iterator(this, true); }
  135. reverse_iterator rbegin() noexcept { return reverse_iterator(this->end()); }
  136. const_reverse_iterator rbegin() const noexcept
  137. {
  138. return const_reverse_iterator(this->end());
  139. }
  140. const_reverse_iterator crbegin() const noexcept
  141. {
  142. return const_reverse_iterator(this->cend());
  143. }
  144. reverse_iterator rend() noexcept { return reverse_iterator(this->begin()); }
  145. const_reverse_iterator rend() const noexcept
  146. {
  147. return const_reverse_iterator(this->begin());
  148. }
  149. const_reverse_iterator crend() const noexcept
  150. {
  151. return const_reverse_iterator(this->cbegin());
  152. }
  153. // Capacity
  154. bool empty() const noexcept { return this->Set.none(); }
  155. size_type size() const noexcept { return this->Set.count(); }
  156. size_type max_size() const noexcept { return this->Set.size(); }
  157. // Modifiers
  158. void clear() noexcept { this->Set.reset(); }
  159. enum_set& operator+=(key_type e)
  160. {
  161. this->insert(e);
  162. return *this;
  163. }
  164. enum_set& operator+=(const enum_set& other) noexcept
  165. {
  166. this->erase(other);
  167. return *this;
  168. }
  169. enum_set& operator+=(std::initializer_list<value_type> list)
  170. {
  171. this->insert(list);
  172. return *this;
  173. }
  174. enum_set& operator-=(key_type e)
  175. {
  176. this->erase(e);
  177. return *this;
  178. }
  179. enum_set& operator-=(const enum_set& other) noexcept
  180. {
  181. this->erase(other);
  182. return *this;
  183. }
  184. enum_set& operator-=(std::initializer_list<value_type> list)
  185. {
  186. this->erase(list);
  187. return *this;
  188. }
  189. std::pair<iterator, bool> insert(value_type value)
  190. {
  191. auto exist = this->contains(value);
  192. if (!exist) {
  193. this->Set.set(static_cast<size_type>(value));
  194. }
  195. return { iterator(this, static_cast<size_type>(value)), !exist };
  196. }
  197. template <typename InputIt>
  198. void insert(InputIt first, InputIt last)
  199. {
  200. for (auto i = first; i != last; i++) {
  201. this->insert(*i);
  202. }
  203. }
  204. void insert(const enum_set& other) noexcept { this->Set |= other.Set; }
  205. void insert(std::initializer_list<value_type> list)
  206. {
  207. for (auto e : list) {
  208. this->Set.set(static_cast<size_type>(e));
  209. }
  210. }
  211. size_type erase(key_type key)
  212. {
  213. if (this->contains(key)) {
  214. this->Set.reset(static_cast<size_type>(key));
  215. return 1;
  216. }
  217. return 0;
  218. }
  219. iterator erase(iterator pos)
  220. {
  221. this->erase(*pos++);
  222. return pos;
  223. }
  224. iterator erase(const_iterator pos)
  225. {
  226. this->erase(*pos++);
  227. return pos == this->cend() ? this->end()
  228. : iterator(this, static_cast<size_type>(*pos));
  229. }
  230. void erase(const enum_set& other) noexcept { this->Set &= ~other.Set; }
  231. void erase(std::initializer_list<value_type> list)
  232. {
  233. for (auto e : list) {
  234. this->Set.reset(static_cast<size_type>(e));
  235. }
  236. }
  237. void swap(enum_set& other) noexcept
  238. {
  239. auto tmp = this->Set;
  240. this->Set = other.Set;
  241. other.Set = tmp;
  242. }
  243. // toggle the specified enum
  244. void flip(key_type key) { this->Set.flip(static_cast<size_type>(key)); }
  245. // toggle all the enums stored in the other enum_set
  246. void flip(const enum_set& other) noexcept { this->Set ^= other.Set; }
  247. // toggle all the enums specified in the list
  248. void flip(std::initializer_list<value_type> list)
  249. {
  250. for (auto e : list) {
  251. this->Set.flip(static_cast<size_type>(e));
  252. }
  253. }
  254. // Lookup
  255. size_type count(key_type e) const { return this->contains(e) ? 1 : 0; }
  256. iterator find(key_type e)
  257. {
  258. if (this->contains(e)) {
  259. return iterator(this, static_cast<size_type>(e));
  260. }
  261. return this->end();
  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. }
  268. return this->end();
  269. }
  270. bool contains(key_type e) const
  271. {
  272. return this->Set.test(static_cast<size_type>(e));
  273. }
  274. private:
  275. template <typename E>
  276. friend inline bool operator==(const enum_set<E>& lhs,
  277. const enum_set<E>& rhs) noexcept;
  278. template <typename E, typename Predicate>
  279. friend inline void erase_if(enum_set<E>& set, Predicate pred);
  280. friend class enum_set_iterator<enum_set>;
  281. friend class enum_set_iterator<const enum_set>;
  282. bool test(size_type pos) const { return this->Set.test(pos); }
  283. std::bitset<std::numeric_limits<size_type>::digits> Set;
  284. };
  285. // non-member functions for enum_set
  286. template <typename Enum>
  287. inline enum_set<Enum> operator+(const enum_set<Enum>& lhs, Enum rhs)
  288. {
  289. return enum_set<Enum>(lhs) += rhs;
  290. }
  291. template <typename Enum>
  292. inline enum_set<Enum> operator+(const enum_set<Enum>& lhs,
  293. const enum_set<Enum>& rhs) noexcept
  294. {
  295. return enum_set<Enum>(lhs) += rhs;
  296. }
  297. template <typename Enum>
  298. inline enum_set<Enum> operator+(const enum_set<Enum>& lhs,
  299. const std::initializer_list<Enum> rhs)
  300. {
  301. return enum_set<Enum>(lhs) += rhs;
  302. }
  303. template <typename Enum>
  304. inline enum_set<Enum> operator-(const enum_set<Enum>& lhs, Enum rhs)
  305. {
  306. return enum_set<Enum>(lhs) -= rhs;
  307. }
  308. template <typename Enum>
  309. inline enum_set<Enum> operator-(const enum_set<Enum>& lhs,
  310. const enum_set<Enum>& rhs) noexcept
  311. {
  312. return enum_set<Enum>(lhs) -= rhs;
  313. }
  314. template <typename Enum>
  315. inline enum_set<Enum> operator-(const enum_set<Enum>& lhs,
  316. const std::initializer_list<Enum> rhs)
  317. {
  318. return enum_set<Enum>(lhs) -= rhs;
  319. }
  320. template <typename Enum>
  321. inline bool operator==(const enum_set<Enum>& lhs,
  322. const enum_set<Enum>& rhs) noexcept
  323. {
  324. return lhs.Set == rhs.Set;
  325. }
  326. template <typename Enum>
  327. inline bool operator!=(const enum_set<Enum>& lhs,
  328. const enum_set<Enum>& rhs) noexcept
  329. {
  330. return !(lhs == rhs);
  331. }
  332. template <typename Enum>
  333. inline void erase(enum_set<Enum>& set, Enum value)
  334. {
  335. set.erase(value);
  336. }
  337. template <typename Enum, typename Predicate>
  338. inline void erase_if(enum_set<Enum>& set, Predicate pred)
  339. {
  340. for (std::size_t index = 0; index < set.Set.size(); ++index) {
  341. if (set.Set.test(index) && pred(static_cast<Enum>(index))) {
  342. set.Set.reset(index);
  343. }
  344. }
  345. }
  346. } // namespace cm