enum_set 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  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 the 'enum class' type with unsigned base type is supported. Moreover,
  16. // all definitions must be specified without a value.
  17. //
  18. // The methods offered by 'enum_set' are close as possible to the 'std::set'
  19. // container as well as the methods from 'std::bitset'.
  20. //
  21. // Internally, this class use 'std::bitset' container to manage the
  22. // set of enum.
  23. //
  24. // The size of the bitset is deduced from the underlying type of
  25. // the enum or can be set explicitly as template parameter:
  26. //
  27. // enum class Example : unsigned { A, B, C, D };
  28. // using ExampleSet = enum_set<Example, 4>;
  29. //
  30. // Another possibility is to add, at the end of the list, the definition
  31. // 'cm_count' with the value of the precedent definition:
  32. //
  33. // enum class Example : unsigned { A, B, C, D, cm_count = D };
  34. // using ExampleSet = enum_set<Example>;
  35. //
  36. // To facilitate the usage of the enum_set, operators '+' and '|' can be used
  37. // as alternate to the 'initializer_list':
  38. //
  39. // auto set1 = Example::A | Example::B | Example::C;
  40. // auto set2 = Example::A + Example::B;
  41. // set2.set(Example::C | Example::D);
  42. //
  43. namespace cm {
  44. namespace internals {
  45. template <typename Enum, typename U = void>
  46. struct enum_size
  47. {
  48. static constexpr auto value =
  49. std::numeric_limits<typename std::underlying_type<Enum>::type>::digits;
  50. };
  51. template <typename Enum>
  52. struct enum_size<Enum, cm::void_t<decltype(Enum::cm_count)>>
  53. {
  54. static constexpr auto value =
  55. static_cast<typename std::underlying_type<Enum>::type>(Enum::cm_count) + 1;
  56. };
  57. }
  58. template <typename EnumSet>
  59. class enum_set_iterator
  60. {
  61. public:
  62. enum_set_iterator() = default;
  63. enum_set_iterator(enum_set_iterator const& other) = default;
  64. using iterator_category = std::bidirectional_iterator_tag;
  65. using value_type = typename EnumSet::value_type;
  66. using difference_type = typename EnumSet::difference_type;
  67. using reference = typename EnumSet::reference;
  68. using pointer = typename EnumSet::pointer;
  69. enum_set_iterator& operator++()
  70. {
  71. while (++this->Index < this->Set->max_size() &&
  72. !this->Set->test(this->Index))
  73. ;
  74. return *this;
  75. }
  76. enum_set_iterator operator++(int)
  77. {
  78. auto retval = *this;
  79. ++(*this);
  80. return retval;
  81. }
  82. enum_set_iterator& operator--()
  83. {
  84. if (this->Index == 0) {
  85. return *this;
  86. }
  87. while (!this->Set->test(--this->Index) && this->Index != 0)
  88. ;
  89. return *this;
  90. }
  91. enum_set_iterator operator--(int)
  92. {
  93. auto retval = *this;
  94. --(*this);
  95. return retval;
  96. }
  97. reference operator*() const { return static_cast<reference>(this->Index); }
  98. bool operator==(enum_set_iterator other) const
  99. {
  100. return (this->Set == other.Set) && (this->Index == other.Index);
  101. }
  102. bool operator!=(enum_set_iterator other) const { return !(*this == other); }
  103. private:
  104. friend EnumSet;
  105. using size_type = typename EnumSet::size_type;
  106. enum_set_iterator(EnumSet* set, bool at_end = false)
  107. : Set(set)
  108. {
  109. if (at_end || this->Set->empty()) {
  110. this->Index = this->Set->max_size();
  111. } else {
  112. while (!this->Set->test(this->Index) &&
  113. ++this->Index < this->Set->max_size())
  114. ;
  115. }
  116. }
  117. enum_set_iterator(EnumSet* set, size_type pos)
  118. : Index(pos)
  119. , Set(set)
  120. {
  121. }
  122. std::size_t Index = 0;
  123. EnumSet* Set = nullptr;
  124. };
  125. template <
  126. typename Enum, std::size_t Size = internals::enum_size<Enum>::value,
  127. typename cm::enable_if_t<
  128. cm::is_scoped_enum<Enum>::value &&
  129. std::is_unsigned<typename std::underlying_type<Enum>::type>::value,
  130. int> = 0>
  131. class enum_set
  132. {
  133. public:
  134. using key_type = Enum;
  135. using value_type = Enum;
  136. using size_type = typename std::underlying_type<Enum>::type;
  137. using difference_type = size_type;
  138. using reference = Enum;
  139. using const_reference = Enum;
  140. using pointer = Enum const*;
  141. using const_pointer = Enum const*;
  142. using iterator = enum_set_iterator<enum_set>;
  143. using const_iterator = enum_set_iterator<enum_set const>;
  144. using reverse_iterator = std::reverse_iterator<iterator>;
  145. using const_reverse_iterator = std::reverse_iterator<const_iterator>;
  146. constexpr enum_set() noexcept = default;
  147. enum_set(key_type e) { this->insert(e); }
  148. enum_set(enum_set const& other) noexcept { this->insert(other); }
  149. enum_set(std::initializer_list<value_type> list) { this->insert(list); }
  150. enum_set& operator=(key_type e)
  151. {
  152. this->Set.reset();
  153. this->insert(e);
  154. return *this;
  155. }
  156. enum_set& operator=(enum_set const& other) noexcept
  157. {
  158. this->Set.reset();
  159. this->Set |= other.Set;
  160. return *this;
  161. }
  162. enum_set& operator=(std::initializer_list<value_type> list)
  163. {
  164. this->Set.reset();
  165. this->insert(list);
  166. return *this;
  167. }
  168. // Iterators
  169. iterator begin() noexcept { return iterator(this); }
  170. const_iterator begin() const noexcept { return const_iterator(this); }
  171. const_iterator cbegin() const noexcept { return const_iterator(this); }
  172. iterator end() noexcept { return iterator(this, true); }
  173. const_iterator end() const noexcept { return const_iterator(this, true); }
  174. const_iterator cend() const noexcept { return const_iterator(this, true); }
  175. reverse_iterator rbegin() noexcept { return reverse_iterator(this->end()); }
  176. const_reverse_iterator rbegin() const noexcept
  177. {
  178. return const_reverse_iterator(this->end());
  179. }
  180. const_reverse_iterator crbegin() const noexcept
  181. {
  182. return const_reverse_iterator(this->cend());
  183. }
  184. reverse_iterator rend() noexcept { return reverse_iterator(this->begin()); }
  185. const_reverse_iterator rend() const noexcept
  186. {
  187. return const_reverse_iterator(this->begin());
  188. }
  189. const_reverse_iterator crend() const noexcept
  190. {
  191. return const_reverse_iterator(this->cbegin());
  192. }
  193. // Capacity
  194. bool empty() const noexcept { return this->Set.none(); }
  195. size_type size() const noexcept { return this->Set.count(); }
  196. size_type max_size() const noexcept { return this->Set.size(); }
  197. // Modifiers
  198. // set all elements
  199. enum_set& set()
  200. {
  201. this->Set.set();
  202. return *this;
  203. }
  204. enum_set& set(key_type e)
  205. {
  206. this->insert(e);
  207. return *this;
  208. }
  209. enum_set& set(enum_set const& other) noexcept
  210. {
  211. this->insert(other);
  212. return *this;
  213. }
  214. enum_set& set(std::initializer_list<value_type> list)
  215. {
  216. this->insert(list);
  217. return *this;
  218. }
  219. // alternate syntax for bit set
  220. enum_set& operator+=(key_type e) { return this->set(e); }
  221. enum_set& operator+=(enum_set const& other) noexcept
  222. {
  223. return this->set(other);
  224. }
  225. enum_set& operator+=(std::initializer_list<value_type> list)
  226. {
  227. return this->set(list);
  228. }
  229. // alternate syntax for bit set
  230. enum_set& operator|=(key_type e) { return this->set(e); }
  231. enum_set& operator|=(enum_set const& other) noexcept
  232. {
  233. return this->set(other);
  234. }
  235. enum_set& operator|=(std::initializer_list<value_type> list)
  236. {
  237. return this->set(list);
  238. }
  239. // reset all elements
  240. void clear() noexcept { this->Set.reset(); }
  241. enum_set& reset()
  242. {
  243. this->Set.reset();
  244. return *this;
  245. }
  246. enum_set& reset(key_type e)
  247. {
  248. this->erase(e);
  249. return *this;
  250. }
  251. enum_set& reset(enum_set const& other) noexcept
  252. {
  253. this->erase(other);
  254. return *this;
  255. }
  256. enum_set& reset(std::initializer_list<value_type> list)
  257. {
  258. this->erase(list);
  259. return *this;
  260. }
  261. // alternate syntax for bit reset
  262. enum_set& operator-=(key_type e) { return this->reset(e); }
  263. enum_set& operator-=(enum_set const& other) noexcept
  264. {
  265. return this->reset(other);
  266. }
  267. enum_set& operator-=(std::initializer_list<value_type> list)
  268. {
  269. return this->reset(list);
  270. }
  271. // toggle the specified enum
  272. enum_set& flip(key_type e)
  273. {
  274. this->Set.flip(static_cast<size_type>(e));
  275. return *this;
  276. }
  277. // toggle all the enums stored in the other enum_set
  278. enum_set& flip(enum_set const& other) noexcept
  279. {
  280. this->Set ^= other.Set;
  281. return *this;
  282. }
  283. // toggle all the enums specified in the list
  284. enum_set& flip(std::initializer_list<value_type> list)
  285. {
  286. for (auto e : list) {
  287. this->Set.flip(static_cast<size_type>(e));
  288. }
  289. return *this;
  290. }
  291. // alternate syntax for bit toggle
  292. enum_set& operator^=(key_type key) { return this->flip(key); }
  293. // toggle all the enums stored in the other enum_set
  294. enum_set& operator^=(enum_set const& other) noexcept
  295. {
  296. return this->flip(other);
  297. }
  298. // toggle all the enums specified in the list
  299. enum_set& operator^=(std::initializer_list<value_type> list)
  300. {
  301. return this->flip(list);
  302. }
  303. std::pair<iterator, bool> insert(key_type value)
  304. {
  305. auto exist = this->contains(value);
  306. if (!exist) {
  307. this->Set.set(static_cast<size_type>(value));
  308. }
  309. return { iterator(this, static_cast<size_type>(value)), !exist };
  310. }
  311. template <typename InputIt>
  312. void insert(InputIt first, InputIt last)
  313. {
  314. for (auto i = first; i != last; i++) {
  315. this->insert(*i);
  316. }
  317. }
  318. void insert(enum_set const& other) noexcept { this->Set |= other.Set; }
  319. void insert(std::initializer_list<value_type> list)
  320. {
  321. for (auto e : list) {
  322. this->Set.set(static_cast<size_type>(e));
  323. }
  324. }
  325. size_type erase(key_type key)
  326. {
  327. if (this->contains(key)) {
  328. this->Set.reset(static_cast<size_type>(key));
  329. return 1;
  330. }
  331. return 0;
  332. }
  333. iterator erase(iterator pos)
  334. {
  335. this->erase(*pos++);
  336. return pos;
  337. }
  338. iterator erase(const_iterator pos)
  339. {
  340. this->erase(*pos++);
  341. return pos == this->cend() ? this->end()
  342. : iterator(this, static_cast<size_type>(*pos));
  343. }
  344. void erase(enum_set const& other) noexcept { this->Set &= ~other.Set; }
  345. void erase(std::initializer_list<value_type> list)
  346. {
  347. for (auto e : list) {
  348. this->Set.reset(static_cast<size_type>(e));
  349. }
  350. }
  351. void swap(enum_set& other) noexcept
  352. {
  353. auto tmp = this->Set;
  354. this->Set = other.Set;
  355. other.Set = tmp;
  356. }
  357. // Lookup
  358. size_type count(key_type e) const { return this->contains(e) ? 1 : 0; }
  359. iterator find(key_type e)
  360. {
  361. if (this->contains(e)) {
  362. return iterator(this, static_cast<size_type>(e));
  363. }
  364. return this->end();
  365. }
  366. const_iterator find(key_type e) const
  367. {
  368. if (this->contains(e)) {
  369. return const_iterator(this, static_cast<size_type>(e));
  370. }
  371. return this->end();
  372. }
  373. // Checks
  374. bool contains(key_type e) const
  375. {
  376. return this->Set.test(static_cast<size_type>(e));
  377. }
  378. bool all() const { return this->Set.all(); }
  379. bool any() const { return this->Set.any(); }
  380. bool none() const { return this->Set.none(); }
  381. // alternate syntax to none()
  382. bool operator!() const { return this->Set.none(); }
  383. bool all_of(enum_set const& set) const
  384. {
  385. auto result = set;
  386. result.Set &= this->Set;
  387. return result == set;
  388. }
  389. bool any_of(enum_set const& set) const
  390. {
  391. auto result = set;
  392. result.Set &= this->Set;
  393. return result.any();
  394. }
  395. bool none_of(enum_set const& set) const
  396. {
  397. auto result = set;
  398. result.Set &= this->Set;
  399. return result.none();
  400. }
  401. private:
  402. template <typename E>
  403. friend inline bool operator==(enum_set<E> const& lhs,
  404. enum_set<E> const& rhs) noexcept;
  405. template <typename E, typename Predicate>
  406. friend inline void erase_if(enum_set<E>& set, Predicate pred);
  407. friend class enum_set_iterator<enum_set>;
  408. friend class enum_set_iterator<enum_set const>;
  409. bool test(size_type pos) const { return this->Set.test(pos); }
  410. std::bitset<Size> Set;
  411. };
  412. // non-member functions for enum_set
  413. template <typename Enum>
  414. inline enum_set<Enum> operator+(enum_set<Enum> const& lhs, Enum rhs)
  415. {
  416. return enum_set<Enum>{ lhs } += rhs;
  417. }
  418. template <typename Enum>
  419. inline enum_set<Enum> operator+(enum_set<Enum> const& lhs,
  420. enum_set<Enum> const& rhs) noexcept
  421. {
  422. return enum_set<Enum>{ lhs } += rhs;
  423. }
  424. template <typename Enum>
  425. inline enum_set<Enum> operator+(enum_set<Enum> const& lhs,
  426. std::initializer_list<Enum> const rhs)
  427. {
  428. return enum_set<Enum>{ lhs } += rhs;
  429. }
  430. template <typename Enum>
  431. inline cm::enum_set<Enum> operator|(cm::enum_set<Enum> const& lhs, Enum rhs)
  432. {
  433. return enum_set<Enum>{ lhs } |= rhs;
  434. }
  435. template <typename Enum>
  436. inline cm::enum_set<Enum> operator|(Enum lhs, cm::enum_set<Enum> const& rhs)
  437. {
  438. return enum_set<Enum>{ lhs } |= rhs;
  439. }
  440. template <typename Enum>
  441. inline cm::enum_set<Enum> operator|(cm::enum_set<Enum> const& lhs,
  442. cm::enum_set<Enum> const& rhs)
  443. {
  444. return enum_set<Enum>{ lhs } |= rhs;
  445. }
  446. template <typename Enum>
  447. inline enum_set<Enum> operator-(enum_set<Enum> const& lhs, Enum rhs)
  448. {
  449. return enum_set<Enum>{ lhs } -= rhs;
  450. }
  451. template <typename Enum>
  452. inline enum_set<Enum> operator-(enum_set<Enum> const& lhs,
  453. enum_set<Enum> const& rhs) noexcept
  454. {
  455. return enum_set<Enum>{ lhs } -= rhs;
  456. }
  457. template <typename Enum>
  458. inline enum_set<Enum> operator-(enum_set<Enum> const& lhs,
  459. std::initializer_list<Enum> const rhs)
  460. {
  461. return enum_set<Enum>{ lhs } -= rhs;
  462. }
  463. template <typename Enum>
  464. inline enum_set<Enum> operator^(enum_set<Enum> const& lhs, Enum rhs)
  465. {
  466. return enum_set<Enum>{ lhs } ^= rhs;
  467. }
  468. template <typename Enum>
  469. inline enum_set<Enum> operator^(enum_set<Enum> const& lhs,
  470. enum_set<Enum> const& rhs) noexcept
  471. {
  472. return enum_set<Enum>{ lhs } ^= rhs;
  473. }
  474. template <typename Enum>
  475. inline enum_set<Enum> operator^(enum_set<Enum> const& lhs,
  476. std::initializer_list<Enum> const rhs)
  477. {
  478. return enum_set<Enum>{ lhs } ^= rhs;
  479. }
  480. template <typename Enum>
  481. inline bool operator==(enum_set<Enum> const& lhs,
  482. enum_set<Enum> const& rhs) noexcept
  483. {
  484. return lhs.Set == rhs.Set;
  485. }
  486. template <typename Enum>
  487. inline bool operator!=(enum_set<Enum> const& lhs,
  488. enum_set<Enum> const& rhs) noexcept
  489. {
  490. return !(lhs == rhs);
  491. }
  492. template <typename Enum>
  493. inline void erase(enum_set<Enum>& set, Enum value)
  494. {
  495. set.erase(value);
  496. }
  497. template <typename Enum, typename Predicate>
  498. inline void erase_if(enum_set<Enum>& set, Predicate pred)
  499. {
  500. for (std::size_t index = 0; index < set.Set.size(); ++index) {
  501. if (set.Set.test(index) && pred(static_cast<Enum>(index))) {
  502. set.Set.reset(index);
  503. }
  504. }
  505. }
  506. } // namespace cm
  507. template <typename Enum,
  508. typename cm::enable_if_t<cm::is_scoped_enum<Enum>::value, int> = 0>
  509. inline cm::enum_set<Enum> operator+(Enum lhs, Enum rhs)
  510. {
  511. return cm::enum_set<Enum>{ lhs, rhs };
  512. }
  513. // Alternate syntax
  514. template <typename Enum,
  515. typename cm::enable_if_t<cm::is_scoped_enum<Enum>::value, int> = 0>
  516. inline cm::enum_set<Enum> operator|(Enum lhs, Enum rhs)
  517. {
  518. return cm::enum_set<Enum>{ lhs, rhs };
  519. }