enum_set 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. // -*-c++-*-
  2. // vim: set ft=cpp:
  3. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  4. file LICENSE.rst 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. // To facilitate the usage of the enum_set, operators '+' and '|' can be used
  31. // as alternate to the 'initializer_list':
  32. //
  33. // auto set1 = Example::A | Example::B | Example::C;
  34. // auto set2 = Example::A + Example::B;
  35. // set2.set(Example::C | Example::D);
  36. //
  37. namespace cm {
  38. template <typename EnumSet>
  39. class enum_set_iterator
  40. {
  41. public:
  42. enum_set_iterator() = default;
  43. enum_set_iterator(enum_set_iterator const& other) = default;
  44. using iterator_category = std::bidirectional_iterator_tag;
  45. using value_type = typename EnumSet::value_type;
  46. using difference_type = typename EnumSet::difference_type;
  47. using reference = typename EnumSet::reference;
  48. using pointer = typename EnumSet::pointer;
  49. enum_set_iterator& operator++()
  50. {
  51. while (++this->Index < this->Set->max_size() &&
  52. !this->Set->test(this->Index))
  53. ;
  54. return *this;
  55. }
  56. enum_set_iterator operator++(int)
  57. {
  58. auto retval = *this;
  59. ++(*this);
  60. return retval;
  61. }
  62. enum_set_iterator& operator--()
  63. {
  64. if (this->Index == 0) {
  65. return *this;
  66. }
  67. while (!this->Set->test(--this->Index) && this->Index != 0)
  68. ;
  69. return *this;
  70. }
  71. enum_set_iterator operator--(int)
  72. {
  73. auto retval = *this;
  74. --(*this);
  75. return retval;
  76. }
  77. reference operator*() const { return static_cast<reference>(this->Index); }
  78. bool operator==(enum_set_iterator other) const
  79. {
  80. return (this->Set == other.Set) && (this->Index == other.Index);
  81. }
  82. bool operator!=(enum_set_iterator other) const { return !(*this == other); }
  83. private:
  84. friend EnumSet;
  85. using size_type = typename EnumSet::size_type;
  86. enum_set_iterator(EnumSet* set, bool at_end = false)
  87. : Set(set)
  88. {
  89. if (at_end || this->Set->empty()) {
  90. this->Index = this->Set->max_size();
  91. } else {
  92. while (!this->Set->test(this->Index) &&
  93. ++this->Index < this->Set->max_size())
  94. ;
  95. }
  96. }
  97. enum_set_iterator(EnumSet* set, size_type pos)
  98. : Index(pos)
  99. , Set(set)
  100. {
  101. }
  102. std::size_t Index = 0;
  103. EnumSet* Set = nullptr;
  104. };
  105. template <
  106. typename Enum,
  107. std::size_t Size =
  108. std::numeric_limits<typename std::underlying_type<Enum>::type>::digits,
  109. typename cm::enable_if_t<
  110. cm::is_scoped_enum<Enum>::value &&
  111. std::is_unsigned<typename std::underlying_type<Enum>::type>::value,
  112. int> = 0>
  113. class enum_set
  114. {
  115. public:
  116. static constexpr std::size_t set_size = Size;
  117. using key_type = Enum;
  118. using value_type = Enum;
  119. using size_type = typename std::underlying_type<Enum>::type;
  120. using difference_type = size_type;
  121. using reference = Enum;
  122. using const_reference = Enum;
  123. using pointer = Enum const*;
  124. using const_pointer = Enum const*;
  125. using iterator = enum_set_iterator<enum_set>;
  126. using const_iterator = enum_set_iterator<enum_set const>;
  127. using reverse_iterator = std::reverse_iterator<iterator>;
  128. using const_reverse_iterator = std::reverse_iterator<const_iterator>;
  129. constexpr enum_set() noexcept {}
  130. enum_set(key_type e) { this->insert(e); }
  131. enum_set(enum_set const& other) noexcept { this->insert(other); }
  132. template <typename E,
  133. typename cm::enable_if_t<std::is_same<Enum, E>::value, int> = 0>
  134. enum_set(enum_set<E> const& other) noexcept
  135. {
  136. static_assert(Size < enum_set<E>::set_size, "Incompatible sizes");
  137. this->insert(other.cbegin(), other.cend());
  138. }
  139. enum_set(std::initializer_list<value_type> list) { this->insert(list); }
  140. enum_set& operator=(key_type e)
  141. {
  142. this->Set.reset();
  143. this->insert(e);
  144. return *this;
  145. }
  146. enum_set& operator=(enum_set const& other) noexcept
  147. {
  148. this->Set.reset();
  149. this->Set |= other.Set;
  150. return *this;
  151. }
  152. enum_set& operator=(std::initializer_list<value_type> list)
  153. {
  154. this->Set.reset();
  155. this->insert(list);
  156. return *this;
  157. }
  158. // Iterators
  159. iterator begin() noexcept { return iterator(this); }
  160. const_iterator begin() const noexcept { return const_iterator(this); }
  161. const_iterator cbegin() const noexcept { return const_iterator(this); }
  162. iterator end() noexcept { return iterator(this, true); }
  163. const_iterator end() const noexcept { return const_iterator(this, true); }
  164. const_iterator cend() const noexcept { return const_iterator(this, true); }
  165. reverse_iterator rbegin() noexcept { return reverse_iterator(this->end()); }
  166. const_reverse_iterator rbegin() const noexcept
  167. {
  168. return const_reverse_iterator(this->end());
  169. }
  170. const_reverse_iterator crbegin() const noexcept
  171. {
  172. return const_reverse_iterator(this->cend());
  173. }
  174. reverse_iterator rend() noexcept { return reverse_iterator(this->begin()); }
  175. const_reverse_iterator rend() const noexcept
  176. {
  177. return const_reverse_iterator(this->begin());
  178. }
  179. const_reverse_iterator crend() const noexcept
  180. {
  181. return const_reverse_iterator(this->cbegin());
  182. }
  183. // Capacity
  184. bool empty() const noexcept { return this->Set.none(); }
  185. size_type size() const noexcept { return this->Set.count(); }
  186. size_type max_size() const noexcept { return this->Set.size(); }
  187. // Modifiers
  188. // set all elements
  189. enum_set& set()
  190. {
  191. this->Set.set();
  192. return *this;
  193. }
  194. enum_set& set(key_type e)
  195. {
  196. this->insert(e);
  197. return *this;
  198. }
  199. enum_set& set(enum_set const& other) noexcept
  200. {
  201. this->insert(other);
  202. return *this;
  203. }
  204. enum_set& set(std::initializer_list<value_type> list)
  205. {
  206. this->insert(list);
  207. return *this;
  208. }
  209. // alternate syntax for bit set
  210. enum_set& operator+=(key_type e) { return this->set(e); }
  211. enum_set& operator+=(enum_set const& other) noexcept
  212. {
  213. return this->set(other);
  214. }
  215. enum_set& operator+=(std::initializer_list<value_type> list)
  216. {
  217. return this->set(list);
  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. // reset all elements
  230. void clear() noexcept { this->Set.reset(); }
  231. enum_set& reset()
  232. {
  233. this->Set.reset();
  234. return *this;
  235. }
  236. enum_set& reset(key_type e)
  237. {
  238. this->erase(e);
  239. return *this;
  240. }
  241. enum_set& reset(enum_set const& other) noexcept
  242. {
  243. this->erase(other);
  244. return *this;
  245. }
  246. enum_set& reset(std::initializer_list<value_type> list)
  247. {
  248. this->erase(list);
  249. return *this;
  250. }
  251. // alternate syntax for bit reset
  252. enum_set& operator-=(key_type e) { return this->reset(e); }
  253. enum_set& operator-=(enum_set const& other) noexcept
  254. {
  255. return this->reset(other);
  256. }
  257. enum_set& operator-=(std::initializer_list<value_type> list)
  258. {
  259. return this->reset(list);
  260. }
  261. // toggle the specified enum
  262. enum_set& flip(key_type e)
  263. {
  264. this->Set.flip(static_cast<size_type>(e));
  265. return *this;
  266. }
  267. // toggle all the enums stored in the other enum_set
  268. enum_set& flip(enum_set const& other) noexcept
  269. {
  270. this->Set ^= other.Set;
  271. return *this;
  272. }
  273. // toggle all the enums specified in the list
  274. enum_set& flip(std::initializer_list<value_type> list)
  275. {
  276. for (auto e : list) {
  277. this->Set.flip(static_cast<size_type>(e));
  278. }
  279. return *this;
  280. }
  281. // alternate syntax for bit toggle
  282. enum_set& operator^=(key_type key) { return this->flip(key); }
  283. // toggle all the enums stored in the other enum_set
  284. enum_set& operator^=(enum_set const& other) noexcept
  285. {
  286. return this->flip(other);
  287. }
  288. // toggle all the enums specified in the list
  289. enum_set& operator^=(std::initializer_list<value_type> list)
  290. {
  291. return this->flip(list);
  292. }
  293. std::pair<iterator, bool> insert(key_type value)
  294. {
  295. auto exist = this->contains(value);
  296. if (!exist) {
  297. this->Set.set(static_cast<size_type>(value));
  298. }
  299. return { iterator(this, static_cast<size_type>(value)), !exist };
  300. }
  301. template <typename InputIt>
  302. void insert(InputIt first, InputIt last)
  303. {
  304. for (auto i = first; i != last; i++) {
  305. this->insert(*i);
  306. }
  307. }
  308. void insert(enum_set const& other) noexcept { this->Set |= other.Set; }
  309. void insert(std::initializer_list<value_type> list)
  310. {
  311. for (auto e : list) {
  312. this->Set.set(static_cast<size_type>(e));
  313. }
  314. }
  315. size_type erase(key_type key)
  316. {
  317. if (this->contains(key)) {
  318. this->Set.reset(static_cast<size_type>(key));
  319. return 1;
  320. }
  321. return 0;
  322. }
  323. iterator erase(iterator pos)
  324. {
  325. this->erase(*pos++);
  326. return pos;
  327. }
  328. iterator erase(const_iterator pos)
  329. {
  330. this->erase(*pos++);
  331. return pos == this->cend() ? this->end()
  332. : iterator(this, static_cast<size_type>(*pos));
  333. }
  334. void erase(enum_set const& other) noexcept { this->Set &= ~other.Set; }
  335. void erase(std::initializer_list<value_type> list)
  336. {
  337. for (auto e : list) {
  338. this->Set.reset(static_cast<size_type>(e));
  339. }
  340. }
  341. void swap(enum_set& other) noexcept
  342. {
  343. auto tmp = this->Set;
  344. this->Set = other.Set;
  345. other.Set = tmp;
  346. }
  347. // Lookup
  348. size_type count(key_type e) const { return this->contains(e) ? 1 : 0; }
  349. iterator find(key_type e)
  350. {
  351. if (this->contains(e)) {
  352. return iterator(this, static_cast<size_type>(e));
  353. }
  354. return this->end();
  355. }
  356. const_iterator find(key_type e) const
  357. {
  358. if (this->contains(e)) {
  359. return const_iterator(this, static_cast<size_type>(e));
  360. }
  361. return this->end();
  362. }
  363. // Checks
  364. bool contains(key_type e) const
  365. {
  366. return this->Set.test(static_cast<size_type>(e));
  367. }
  368. bool all() const { return this->Set.all(); }
  369. bool any() const { return this->Set.any(); }
  370. bool none() const { return this->Set.none(); }
  371. // alternate syntax to none()
  372. bool operator!() const { return this->Set.none(); }
  373. bool all_of(enum_set const& set) const
  374. {
  375. auto result = set;
  376. result.Set &= this->Set;
  377. return result == set;
  378. }
  379. bool any_of(enum_set const& set) const
  380. {
  381. auto result = set;
  382. result.Set &= this->Set;
  383. return result.any();
  384. }
  385. bool none_of(enum_set const& set) const
  386. {
  387. auto result = set;
  388. result.Set &= this->Set;
  389. return result.none();
  390. }
  391. private:
  392. template <typename E, std::size_t S>
  393. friend inline bool operator==(enum_set<E, S> const& lhs,
  394. enum_set<E, S> const& rhs) noexcept;
  395. template <typename E, std::size_t S, typename Predicate>
  396. friend inline void erase_if(enum_set<E, S>& set, Predicate pred);
  397. friend class enum_set_iterator<enum_set>;
  398. friend class enum_set_iterator<enum_set const>;
  399. bool test(size_type pos) const { return this->Set.test(pos); }
  400. std::bitset<Size> Set;
  401. };
  402. // non-member functions for enum_set
  403. template <typename Enum, std::size_t Size>
  404. inline enum_set<Enum, Size> operator+(enum_set<Enum, Size> const& lhs,
  405. Enum rhs)
  406. {
  407. return enum_set<Enum, Size>{ lhs } += rhs;
  408. }
  409. template <typename Enum, std::size_t Size>
  410. inline enum_set<Enum, Size> operator+(enum_set<Enum, Size> const& lhs,
  411. enum_set<Enum, Size> const& rhs) noexcept
  412. {
  413. return enum_set<Enum, Size>{ lhs } += rhs;
  414. }
  415. template <typename Enum, std::size_t Size>
  416. inline enum_set<Enum, Size> operator+(enum_set<Enum, Size> const& lhs,
  417. std::initializer_list<Enum> const rhs)
  418. {
  419. return enum_set<Enum, Size>{ lhs } += rhs;
  420. }
  421. template <typename Enum, std::size_t Size>
  422. inline cm::enum_set<Enum, Size> operator|(cm::enum_set<Enum, Size> const& lhs,
  423. Enum rhs)
  424. {
  425. return enum_set<Enum, Size>{ lhs } |= rhs;
  426. }
  427. template <typename Enum, std::size_t Size>
  428. inline cm::enum_set<Enum, Size> operator|(Enum lhs,
  429. cm::enum_set<Enum, Size> const& rhs)
  430. {
  431. return enum_set<Enum, Size>{ lhs } |= rhs;
  432. }
  433. template <typename Enum, std::size_t Size>
  434. inline cm::enum_set<Enum, Size> operator|(cm::enum_set<Enum, Size> const& lhs,
  435. cm::enum_set<Enum, Size> const& rhs)
  436. {
  437. return enum_set<Enum, Size>{ lhs } |= rhs;
  438. }
  439. template <typename Enum, std::size_t Size>
  440. inline enum_set<Enum, Size> operator-(enum_set<Enum, Size> const& lhs,
  441. Enum rhs)
  442. {
  443. return enum_set<Enum, Size>{ lhs } -= rhs;
  444. }
  445. template <typename Enum, std::size_t Size>
  446. inline enum_set<Enum, Size> operator-(enum_set<Enum, Size> const& lhs,
  447. enum_set<Enum, Size> const& rhs) noexcept
  448. {
  449. return enum_set<Enum, Size>{ lhs } -= rhs;
  450. }
  451. template <typename Enum, std::size_t Size>
  452. inline enum_set<Enum, Size> operator-(enum_set<Enum, Size> const& lhs,
  453. std::initializer_list<Enum> const rhs)
  454. {
  455. return enum_set<Enum, Size>{ lhs } -= rhs;
  456. }
  457. template <typename Enum, std::size_t Size>
  458. inline enum_set<Enum, Size> operator^(enum_set<Enum, Size> const& lhs,
  459. Enum rhs)
  460. {
  461. return enum_set<Enum, Size>{ lhs } ^= rhs;
  462. }
  463. template <typename Enum, std::size_t Size>
  464. inline enum_set<Enum, Size> operator^(enum_set<Enum, Size> const& lhs,
  465. enum_set<Enum, Size> const& rhs) noexcept
  466. {
  467. return enum_set<Enum, Size>{ lhs } ^= rhs;
  468. }
  469. template <typename Enum, std::size_t Size>
  470. inline enum_set<Enum, Size> operator^(enum_set<Enum, Size> const& lhs,
  471. std::initializer_list<Enum> const rhs)
  472. {
  473. return enum_set<Enum, Size>{ lhs } ^= rhs;
  474. }
  475. template <typename Enum, std::size_t Size>
  476. inline bool operator==(enum_set<Enum, Size> const& lhs,
  477. enum_set<Enum, Size> const& rhs) noexcept
  478. {
  479. return lhs.Set == rhs.Set;
  480. }
  481. template <typename Enum, std::size_t Size>
  482. inline bool operator!=(enum_set<Enum, Size> const& lhs,
  483. enum_set<Enum, Size> const& rhs) noexcept
  484. {
  485. return !(lhs == rhs);
  486. }
  487. template <typename Enum, std::size_t Size>
  488. inline void erase(enum_set<Enum, Size>& set, Enum value)
  489. {
  490. set.erase(value);
  491. }
  492. template <typename Enum, std::size_t Size, typename Predicate>
  493. inline void erase_if(enum_set<Enum, Size>& set, Predicate pred)
  494. {
  495. for (std::size_t index = 0; index < set.Set.size(); ++index) {
  496. if (set.Set.test(index) && pred(static_cast<Enum>(index))) {
  497. set.Set.reset(index);
  498. }
  499. }
  500. }
  501. } // namespace cm
  502. //
  503. // WARNING: the following two operators rely on the enum_set_traits<Enum>
  504. // struct definition.
  505. // The macro CM_ENUM_SET_TRAITS(EnumSet) can be used to define this structure.
  506. //
  507. // Notes:
  508. // When CM_ENUM_SET_TRAITS is used, the following restrictions applies:
  509. // * Due to language constraints, the enum_set_traits specialization must
  510. // occur outside of any namespace or function definition.
  511. // * Only one enum_set instantiation is supported per enum class type.
  512. //
  513. template <typename Enum>
  514. struct cm_enum_set_traits
  515. {
  516. };
  517. namespace cm {
  518. template <typename Enum, typename = cm::void_t<>>
  519. struct is_enum_set : std::false_type
  520. {
  521. };
  522. template <typename Enum>
  523. struct is_enum_set<Enum, cm::void_t<typename cm_enum_set_traits<Enum>::type>>
  524. : std::true_type
  525. {
  526. };
  527. }
  528. #if defined(__SUNPRO_CC) && defined(__sparc)
  529. // Oracle DeveloperStudio C++ compiler on Solaris/Sparc crash on the following
  530. // template declarations, so declare explicitly the operators.
  531. // Helper macro to define the enum_set_traits struct specialization.
  532. # define CM_ENUM_SET_TRAITS(E) \
  533. template <> \
  534. struct cm_enum_set_traits<E::value_type> \
  535. { \
  536. using type = E; \
  537. using value_type = E::value_type; \
  538. }; \
  539. \
  540. inline E operator+(E::value_type lhs, E::value_type rhs) \
  541. { \
  542. return { lhs, rhs }; \
  543. } \
  544. \
  545. inline E operator|(E::value_type lhs, E::value_type rhs) \
  546. { \
  547. return { lhs, rhs }; \
  548. }
  549. #else
  550. // Helper macro to define the enum_set_traits struct specialization.
  551. # define CM_ENUM_SET_TRAITS(E) \
  552. template <> \
  553. struct cm_enum_set_traits<E::value_type> \
  554. { \
  555. using type = E; \
  556. using value_type = E::value_type; \
  557. };
  558. template <typename Enum,
  559. typename cm::enable_if_t<cm::is_enum_set<Enum>::value, int> = 0>
  560. inline typename cm_enum_set_traits<Enum>::type operator+(Enum lhs, Enum rhs)
  561. {
  562. return { lhs, rhs };
  563. }
  564. // Alternate syntax
  565. template <typename Enum,
  566. typename cm::enable_if_t<cm::is_enum_set<Enum>::value, int> = 0>
  567. inline typename cm_enum_set_traits<Enum>::type operator|(Enum lhs, Enum rhs)
  568. {
  569. return { lhs, rhs };
  570. }
  571. #endif