Coverage Report

Created: 2026-02-09 06:05

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