header_utils
Loading...
Searching...
No Matches
enum_flags.h
1
4
5#pragma once
6
7#include "flag_bits.h"
8#include <bit>
9#include <iterator>
10
14
15namespace ghassanpl
16{
17 template <integral_or_enum ENUM, detail::valid_integral VALUE_TYPE = unsigned long long>
18 struct enum_flag_changes;
19
20 constexpr inline struct all_flags_t {} all_flags;
21 constexpr inline struct no_flags_t {} no_flags;
22
27
58 template <integral_or_enum ENUM, detail::valid_integral VALUE_TYPE = unsigned long long>
59 struct enum_flags
60 {
62 using value_type = VALUE_TYPE;
63
65 using enum_type = ENUM;
66 using self_type = enum_flags;
67
68 value_type bits = 0;
69
70 constexpr enum_flags() noexcept = default;
71 constexpr enum_flags(const enum_flags&) noexcept = default;
72 constexpr enum_flags(enum_flags&&) noexcept = default;
73 constexpr enum_flags& operator=(const enum_flags&) noexcept = default;
74 constexpr enum_flags& operator=(enum_flags&&) noexcept = default;
75
76 constexpr enum_flags(enum_type base_value) noexcept : bits(flag_bits<VALUE_TYPE>(base_value)) {}
77 template<typename U = VALUE_TYPE>
78 requires (!std::is_same_v<U, enum_type>)
79 constexpr explicit enum_flags(value_type value) noexcept
80 : bits(value)
81 {
82 }
83
85 template <typename... ARGS>
86 constexpr enum_flags(enum_type base_value, ARGS... args) noexcept : bits(flag_bits<VALUE_TYPE>(base_value, args...)) {}
87
88 constexpr enum_flags(std::initializer_list<enum_type> values) noexcept {
89 for (auto val : values) { bits |= flag_bits<VALUE_TYPE>(val); }
90 }
91
93 template <integral_or_enum BITS_TYPE>
94 requires std::convertible_to<decltype(detail::to_underlying_type(BITS_TYPE{})), VALUE_TYPE>
95 [[nodiscard]] constexpr static self_type from_bits(BITS_TYPE val) noexcept {
96 self_type ret;
97 ret.bits = static_cast<VALUE_TYPE>(detail::to_underlying_type(val));
98 return ret;
99 }
100
102 [[nodiscard]]
103 constexpr static self_type all() noexcept { return self_type::from_bits(~VALUE_TYPE{ 0 }); }
104
105 constexpr enum_flags(all_flags_t) noexcept : bits(~VALUE_TYPE{0}) {}
106
108 [[nodiscard]]
109 constexpr static self_type all(enum_type last) noexcept { return self_type::from_bits(flag_bits<VALUE_TYPE>(last) & ~(flag_bits<VALUE_TYPE>(last + 1) - 1)); }
110
112 [[nodiscard]]
113 constexpr static self_type all_between(enum_type first, enum_type last) noexcept { return all(last) - self_type::from_bits(~(flag_bits<VALUE_TYPE>(first) - 1)); }
114
116 [[nodiscard]]
117 constexpr static self_type none() noexcept { return {}; }
118
119 constexpr enum_flags(no_flags_t) noexcept : bits(VALUE_TYPE{0}) {}
120
122 [[nodiscard]]
123 constexpr bool is_set(enum_type flag) const noexcept { return (bits & flag_bits<VALUE_TYPE>(flag)) != 0; }
124
126 [[nodiscard]]
127 constexpr bool contain(enum_type flag) const noexcept { return this->is_set(flag); }
129 [[nodiscard]]
130 constexpr bool contains(enum_type flag) const noexcept { return this->is_set(flag); }
131
134 template <std::convertible_to<enum_type>... FLAGS>
135 constexpr auto get(enum_type flag1, enum_type flag2, FLAGS... rest)
136 {
137 return std::array{ contain(flag1), contain(flag2), contain(rest)... };
138 }
139
141 [[nodiscard]]
142 constexpr int count() const noexcept { return std::popcount(bits); }
143
146 [[nodiscard]]
147 constexpr enum_type nth_set(size_t n) const noexcept {
148 auto b = bits;
149 while (n--) { b ^= (VALUE_TYPE{ 1 } << std::countr_zero(b)); }
150 return static_cast<enum_type>(std::countr_zero(b));
151 }
152
154 [[nodiscard]]
155 constexpr enum_type first_set() const noexcept { return static_cast<enum_type>(std::countr_zero(bits)); }
156
158 [[nodiscard]]
159 constexpr enum_type last_set() const noexcept { return static_cast<enum_type>((sizeof(value_type) * CHAR_BIT - std::countl_zero(bits)) - 1); }
160
162 template <typename... ARGS>
163 [[nodiscard]]
164 constexpr bool contains_any_of(enum_type arg, ARGS... args) const noexcept
165 {
166 return this->is_set(arg) || (this->is_set(args) || ...);
167 }
168
170 template <typename... ARGS>
171 [[nodiscard]]
172 constexpr bool contains_none_of(enum_type arg, ARGS... args) const noexcept
173 {
174 return !(this->is_set(arg) || (this->is_set(args) || ...));
175 }
176
178 [[nodiscard]]
179 constexpr bool are_any_set() const noexcept { return bits != 0; }
180
182 [[nodiscard]]
183 constexpr bool full() const noexcept { return (~bits) == 0; }
184
187 [[nodiscard]]
188 constexpr bool contains_any_of(self_type other) const noexcept { return other.bits == 0 /* empty set */ || (bits & other.bits) != 0; }
189
191 template <typename... ARGS>
192 [[nodiscard]]
193 constexpr bool contains_all_of(ARGS... args) const noexcept
194 {
195 return (this->is_set(args) && ...);
196 }
197
199 [[nodiscard]]
200 constexpr bool contains_all_of(self_type other) const noexcept { return (bits & other.bits) == other.bits; }
201
202 constexpr explicit operator bool() const noexcept { return bits != 0; }
203
205 [[nodiscard]]
206 constexpr enum_type to_enum_type() const noexcept { return (enum_type)bits; }
207
209 template <std::convertible_to<enum_type>... ARGS>
210 constexpr self_type& set(ARGS... args) noexcept { bits |= flag_bits<VALUE_TYPE>(args...); return *this; }
212 constexpr self_type& set(self_type other) noexcept { bits |= other.bits; return *this; }
213
215 template <std::convertible_to<enum_type>... ARGS>
216 constexpr self_type& unset(ARGS... args) noexcept { bits &= ~ flag_bits<VALUE_TYPE>(args...); return *this; }
218 constexpr self_type& unset(self_type other) noexcept { bits &= ~other.bits; return *this; }
219
221 template <std::convertible_to<enum_type>... ARGS>
222 constexpr self_type& toggle(ARGS... args) noexcept { bits ^= flag_bits<VALUE_TYPE>(args...); return *this; }
224 constexpr self_type& toggle(self_type other) noexcept { bits ^= other.bits; return *this; }
225
227 template <std::convertible_to<enum_type>... ARGS>
228 constexpr self_type& set_to(bool val, ARGS... args) noexcept
229 {
230 if (val) bits |= flag_bits<VALUE_TYPE>(args...); else bits &= ~flag_bits<VALUE_TYPE>(args...);
231 return *this;
232 }
233
235 constexpr self_type& set_to(bool val, self_type other) noexcept
236 {
237 if (val) bits |= other.bits; else bits &= ~other.bits;
238 return *this;
239 }
240
242 [[nodiscard]]
243 constexpr self_type but_only(self_type flags) const noexcept { return self_type::from_bits(bits & flags.bits); }
244
246 [[nodiscard]]
247 constexpr self_type intersected_with(self_type flags) const noexcept { return self_type::from_bits(bits & flags.bits); }
248
249 [[nodiscard]]
250 constexpr self_type operator+(enum_type flag) const noexcept { return self_type::from_bits(bits | flag_bits<VALUE_TYPE>(flag)); }
251 [[nodiscard]]
252 constexpr self_type operator-(enum_type flag) const noexcept { return self_type::from_bits(bits & ~ flag_bits<VALUE_TYPE>(flag)); }
253
254 [[nodiscard]]
255 constexpr self_type operator+(self_type flags) const noexcept { return self_type::from_bits(bits | flags.bits); }
256 [[nodiscard]]
257 constexpr self_type operator-(self_type flags) const noexcept { return self_type::from_bits(bits & ~flags.bits); }
258 [[nodiscard]]
259 constexpr self_type operator-() const noexcept { return self_type::from_bits(~bits); }
260 [[nodiscard]]
261 constexpr self_type except_for(self_type flags) const noexcept { return self_type::from_bits(bits & ~flags.bits); }
262 [[nodiscard]]
263 constexpr self_type without(self_type flags) const noexcept { return self_type::from_bits(bits & ~flags.bits); }
264
265 template <std::convertible_to<enum_type>... ARGS>
266 [[nodiscard]] constexpr self_type except_for(ARGS... args) const noexcept { return self_type::from_bits(bits & ~flag_bits<VALUE_TYPE>(args...)); }
267 template <std::convertible_to<enum_type>... ARGS>
268 [[nodiscard]] constexpr self_type without(ARGS... args) const noexcept { return self_type::from_bits(bits & ~flag_bits<VALUE_TYPE>(args...)); }
269
270 constexpr self_type& operator+=(enum_type flag) noexcept { bits |= flag_bits<VALUE_TYPE>(flag); return *this; }
271 constexpr self_type& operator-=(enum_type flag) noexcept { bits &= ~ flag_bits<VALUE_TYPE>(flag); return *this; }
272
273 constexpr self_type& operator+=(self_type flags) noexcept { bits |= flags.bits; return *this; }
274 constexpr self_type& operator-=(self_type flags) noexcept { bits &= ~flags.bits; return *this; }
275
276 constexpr auto operator<=>(enum_flags const& other) const noexcept = default;
277
278 struct iterator
279 {
281 using bitset_type = std::make_unsigned_t<self_type::value_type>;
282
283 using difference_type = int;
284 using value_type = enum_type;
285 using pointer_type = void;
286 using reference = void;
287 using iterator_category = std::forward_iterator_tag;
288
289
290 enum_type operator*() const noexcept
291 {
292 const auto bitset = static_cast<bitset_type>(bits);
293#pragma warning(suppress: 4146)
294 return static_cast<enum_type>(std::countr_zero(static_cast<bitset_type>(bitset & -bitset)));
295 }
296
297 iterator& operator++() noexcept
298 {
299 const auto bitset = static_cast<bitset_type>(bits);
300#pragma warning(suppress: 4146)
301 const auto t = static_cast<bitset_type>(bitset & -bitset);
302 bits = static_cast<self_type::value_type>(bitset ^ t);
303 return *this;
304 }
305
306 iterator operator++(int) noexcept { const auto result = *this; operator++(); return result; }
307
308 auto operator<=>(iterator const& other) const noexcept = default;
309 };
310
311 iterator begin() const noexcept { return iterator{ bits }; }
312 iterator end() const noexcept { return iterator{ {} }; }
313 iterator cbegin() const noexcept { return iterator{ bits }; }
314 iterator cend() const noexcept { return iterator{ {} }; }
315
316 constexpr bool empty() const noexcept { return bits == 0; }
317 constexpr void clear() noexcept { bits = 0; }
318
325 template <typename FUNC>
326 constexpr auto for_each(FUNC&& callback) const
327 {
328 using return_type = std::invoke_result_t<FUNC, enum_type>;
329 using bitset_type = std::make_unsigned_t<value_type>;
330 auto bitset = static_cast<bitset_type>(bits);
331 while (bitset)
332 {
333#pragma warning(suppress: 4146)
334 const auto t = static_cast<bitset_type>(bitset & -bitset);
335 const auto r = std::countr_zero(t);
336 if constexpr (std::is_convertible_v<return_type, bool>)
337 {
338 if (auto ret = callback((enum_type)r)) return ret;
339 }
340 else
341 callback((enum_type)r);
342 bitset ^= t;
343 }
344 if constexpr (std::is_convertible_v<return_type, bool>)
345 return return_type{};
346 }
347
348 using flag_changes = enum_flag_changes<enum_type, value_type>;
349
350 constexpr void apply(flag_changes changes) noexcept
351 {
352 const auto bits_to_xor = changes.bits_to_set.bits & changes.bits_to_unset.bits;
353 bits ^= bits_to_xor;
354
356 changes.bits_to_set.bits &= ~bits_to_xor;
357 changes.bits_to_unset.bits &= ~bits_to_xor;
358
360 bits &= ~changes.bits_to_unset.bits;
361 bits |= changes.bits_to_set.bits;
362 }
363
364 constexpr self_type operator+(flag_changes changes) const noexcept
365 {
366 auto result = *this;
367 result.apply(changes);
368 return result;
369 }
370
371 };
372
373 template <typename TYPE>
374 struct is_enum_flags : std::false_type {};
375
376 template <typename ENUM, typename VALUE_TYPE>
377 struct is_enum_flags<enum_flags<ENUM, VALUE_TYPE>> : std::true_type {};
378
379 template <typename TYPE>
380 constexpr bool is_enum_flags_v = is_enum_flags<TYPE>::value;
381
384 enum class enum_flag_change : uint8_t
385 {
386 no_change,
387 set,
388 unset,
389 toggle,
390 };
391
394 template <integral_or_enum ENUM, detail::valid_integral VALUE_TYPE>
395 struct enum_flag_changes
396 {
398 using value_type = VALUE_TYPE;
399
401 using enum_type = ENUM;
402 using self_type = enum_flag_changes;
403
404 using flags_type = enum_flags<ENUM, VALUE_TYPE>;
405
406 constexpr enum_flag_changes() noexcept = default;
407 constexpr enum_flag_changes(enum_flag_changes const&) noexcept = default;
408 constexpr enum_flag_changes(enum_flag_changes&&) noexcept = default;
409 constexpr enum_flag_changes& operator=(enum_flag_changes const&) noexcept = default;
410 constexpr enum_flag_changes& operator=(enum_flag_changes&&) noexcept = default;
411
412 static constexpr self_type no_changes() noexcept { return self_type{}; }
413
414 template <std::convertible_to<enum_type>... ARGS>
415 static constexpr self_type to_set(ARGS... args) noexcept { return self_type{ flags_type{args...}, flags_type{} }; }
416 static constexpr self_type to_set(flags_type other) noexcept { return self_type{ other, flags_type{} }; }
417 static constexpr self_type to_set_all() noexcept { return self_type{ flags_type::all(), flags_type{}}; }
418 template <std::convertible_to<enum_type>... ARGS>
419 static constexpr self_type to_unset(ARGS... args) noexcept { return self_type{ flags_type{}, flags_type{args...} }; }
420 static constexpr self_type to_unset(flags_type other) noexcept { return self_type{ flags_type{}, other }; }
421 static constexpr self_type to_unset_all() noexcept { return self_type{ flags_type{}, flags_type::all() }; }
422 template <std::convertible_to<enum_type>... ARGS>
423 static constexpr self_type to_toggle(ARGS... args) noexcept { return self_type{ flags_type{args...}, flags_type{args...} }; }
424 static constexpr self_type to_toggle(flags_type other) noexcept { return self_type{ other, other }; }
425 static constexpr self_type to_toggle_all() noexcept { return self_type{ flags_type::all(), flags_type::all() }; }
426
427 template <std::convertible_to<enum_type>... ARGS>
428 constexpr self_type& set_change_of(enum_flag_change change, ARGS... args) noexcept
429 {
430 switch (change)
431 {
432 case enum_flag_change::no_change: return dont_change(args...);
433 case enum_flag_change::set: return set(args...);
434 case enum_flag_change::unset: return unset(args...);
435 case enum_flag_change::toggle: return toggle(args...);
436 }
437#if defined(__cpp_lib_unreachable) && __cpp_lib_unreachable >= 202202L
438 std::unreachable();
439#endif
440 }
441
442 constexpr enum_flag_change change_of(enum_type flag) const noexcept
443 {
444 return static_cast<enum_flag_change>(bits_to_set.is_set(flag) * 1 + bits_to_unset.is_set(flag) * 2);
445 }
446
447 constexpr flags_type flags_to_set() const noexcept { return bits_to_set - bits_to_unset; }
448 constexpr flags_type flags_to_unset() const noexcept { return bits_to_unset - bits_to_set; }
449 constexpr flags_type flags_to_toggle() const noexcept { return bits_to_unset.but_only(bits_to_set); }
450 constexpr flags_type flags_to_not_change() const noexcept { return -(bits_to_set + bits_to_unset); }
451
452 constexpr flags_type flags_to(enum_flag_change change) const noexcept
453 {
454 switch (change)
455 {
456 case enum_flag_change::set: return flags_to_set();
457 case enum_flag_change::unset: return flags_to_unset();
458 case enum_flag_change::toggle: return flags_to_toggle();
459 case enum_flag_change::no_change: return flags_to_not_change();
460 }
461#if defined(__cpp_lib_unreachable) && __cpp_lib_unreachable >= 202202L
462 std::unreachable();
463#endif
464 }
465
466
467 template <std::convertible_to<enum_type>... ARGS>
468 constexpr self_type& set(ARGS... args) noexcept { bits_to_set.set(args...); bits_to_unset.unset(args...); return *this; }
469 constexpr self_type& set(flags_type other) noexcept { bits_to_set.set(other); bits_to_unset.unset(other); return *this; }
470
471 template <std::convertible_to<enum_type>... ARGS>
472 constexpr self_type& unset(ARGS... args) noexcept { bits_to_set.unset(args...); bits_to_unset.set(args...); return *this; }
473 constexpr self_type& unset(flags_type other) noexcept { bits_to_set.unset(other); bits_to_unset.set(other); return *this; }
474
475 template <std::convertible_to<enum_type>... ARGS>
476 constexpr self_type& toggle(ARGS... args) noexcept { bits_to_set.set(args...); bits_to_unset.set(args...); return *this; }
477 constexpr self_type& toggle(flags_type other) noexcept { bits_to_set.set(other); bits_to_unset.set(other); return *this; }
478
479 template <std::convertible_to<enum_type>... ARGS>
480 constexpr self_type& dont_change(ARGS... args) noexcept { bits_to_set.unset(args...); bits_to_unset.unset(args...); return *this; }
481 constexpr self_type& dont_change(flags_type other) noexcept { bits_to_set.unset(other); bits_to_unset.unset(other); return *this; }
482
483 constexpr self_type& dont_change_any() noexcept { bits_to_set.clear(); bits_to_unset.clear(); return *this; }
484
485 constexpr auto operator<=>(self_type const& other) const noexcept = default;
486
487 flags_type bits_to_set{};
488 flags_type bits_to_unset{};
489
490 private:
491
492 constexpr enum_flag_changes(flags_type to_set, flags_type to_unset) noexcept
493 : bits_to_set(to_set)
494 , bits_to_unset(to_unset)
495 {
496 }
497
498 };
499
500}
enum_flag_change
A change to be done to an enum flag in an enum_flags set.
Definition enum_flags.h:385
constexpr RESULT_TYPE flag_bits(ARGS... args) noexcept
Takes a list of enum values that represent bit numbers (not actual bit values) and returns a value wi...
Definition flag_bits.h:60
auto iterator(T &cont, RANDOM &rng=::ghassanpl::random::default_random_engine)
Returns an iterator to a random element in cont.
Definition random.h:300
Primary namespace for everything in this library.
Definition align+rec2.h:10
Holds a set of changes to be made to an enum_flags<ENUM, VALUE_TYPE> - conceptually holds a set of st...
Definition enum_flags.h:396
A (constexpr) value struct that represents a set of bits mapped to an enum (implemented as a bitset).
Definition enum_flags.h:60
constexpr bool contains_none_of(enum_type arg, ARGS... args) const noexcept
Returns whether or not any of the given flags are set.
Definition enum_flags.h:172
static constexpr self_type none() noexcept
Returns a value with none of the bits set.
Definition enum_flags.h:117
constexpr enum_flags(enum_type base_value, ARGS... args) noexcept
Constructs the value with all the given flags set.
Definition enum_flags.h:86
constexpr self_type & set_to(bool val, self_type other) noexcept
Sets the flags in the other set to val.
Definition enum_flags.h:235
constexpr self_type & set(self_type other) noexcept
Sets the flags in the other.
Definition enum_flags.h:212
constexpr bool contains(enum_type flag) const noexcept
Same as is_set.
Definition enum_flags.h:130
static constexpr self_type all_between(enum_type first, enum_type last) noexcept
Returns a value with bits starting with first, up to and including last, set.
Definition enum_flags.h:113
constexpr self_type & unset(ARGS... args) noexcept
Unsets the given flags.
Definition enum_flags.h:216
constexpr self_type & toggle(ARGS... args) noexcept
Toggles the given flags.
Definition enum_flags.h:222
constexpr bool are_any_set() const noexcept
Returns whether or not any of the given flags are set.
Definition enum_flags.h:179
ENUM enum_type
Type of the enum that is the source of the flags.
Definition enum_flags.h:65
constexpr enum_type nth_set(size_t n) const noexcept
Returns the value of the nth set bit in the set Preconditions: n must be less than the number of bits...
Definition enum_flags.h:147
constexpr self_type & set(ARGS... args) noexcept
Sets the given flags.
Definition enum_flags.h:210
constexpr bool contains_all_of(ARGS... args) const noexcept
Returns whether or not all of the given flags are set.
Definition enum_flags.h:193
constexpr auto for_each(FUNC &&callback) const
Calls callback for each enum flag in the set.
Definition enum_flags.h:326
static constexpr self_type all() noexcept
Returns a value with all bits set (including the ones not in the enum, if any).
Definition enum_flags.h:103
static constexpr self_type all(enum_type last) noexcept
Returns a value with all bits set, up to and including the last.
Definition enum_flags.h:109
constexpr enum_type to_enum_type() const noexcept
Returns the underlying value representing this set cast to the enum_type.
Definition enum_flags.h:206
constexpr enum_type first_set() const noexcept
Returns the lowest numerical value in the set. Returns an unspecified value if no values are in the s...
Definition enum_flags.h:155
constexpr self_type & toggle(self_type other) noexcept
Toggles the flags in the other set.
Definition enum_flags.h:224
constexpr int count() const noexcept
Returns the number of flags set.
Definition enum_flags.h:142
constexpr bool full() const noexcept
Returns whether *this == all().
Definition enum_flags.h:183
VALUE_TYPE value_type
The underlying integral value type that holds the bits representing the flags.
Definition enum_flags.h:62
constexpr bool contain(enum_type flag) const noexcept
Same as is_set.
Definition enum_flags.h:127
constexpr bool is_set(enum_type flag) const noexcept
Returns whether or not flag is set.
Definition enum_flags.h:123
constexpr self_type & unset(self_type other) noexcept
Unsets the flags in the other set.
Definition enum_flags.h:218
constexpr self_type & set_to(bool val, ARGS... args) noexcept
Sets the given flags to val.
Definition enum_flags.h:228
constexpr bool contains_all_of(self_type other) const noexcept
Returns whether or not all of the flags in the other set are set.
Definition enum_flags.h:200
constexpr self_type intersected_with(self_type flags) const noexcept
Returns a value with our bits from only the flags that are also set in flags (an intersection).
Definition enum_flags.h:247
constexpr self_type but_only(self_type flags) const noexcept
Returns a value with our bits from only the flags that are also set in flags (an intersection).
Definition enum_flags.h:243
static constexpr self_type from_bits(BITS_TYPE val) noexcept
Creates the enum_flags set from the given underlying bits.
Definition enum_flags.h:95
constexpr enum_type last_set() const noexcept
Returns the highest numerical value in the set, or enum_type(-1) if no values are in the set.
Definition enum_flags.h:159
constexpr bool contains_any_of(enum_type arg, ARGS... args) const noexcept
Returns whether or not any of the given flags are set.
Definition enum_flags.h:164
constexpr bool contains_any_of(self_type other) const noexcept
Returns whether or not any of the flags in the other set are set.
Definition enum_flags.h:188
constexpr auto get(enum_type flag1, enum_type flag2, FLAGS... rest)
Returns an array of bools that are set to the value of contains(param) for each flag parameter Useful...
Definition enum_flags.h:135