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
49 template <integral_or_enum ENUM, detail::valid_integral VALUE_TYPE = unsigned long long>
50 struct enum_flags
51 {
53 using value_type = VALUE_TYPE;
54
56 using enum_type = ENUM;
57 using self_type = enum_flags;
58
59 value_type bits = 0;
60
61 constexpr enum_flags() noexcept = default;
66
68 constexpr explicit enum_flags(value_type value) noexcept : bits(value) {}
69
71 template <typename... ARGS>
73
75 [[nodiscard]]
76 constexpr static self_type from_bits(value_type val) noexcept {
78 ret.bits = val;
79 return ret;
80 }
81
83 [[nodiscard]]
84 constexpr static self_type all() noexcept { return self_type::from_bits(~VALUE_TYPE{ 0 }); }
85
86 constexpr enum_flags(all_flags_t) noexcept : bits(~VALUE_TYPE{0}) {}
87
89 [[nodiscard]]
91
93 [[nodiscard]]
94 constexpr static self_type none() noexcept { return {}; }
95
96 constexpr enum_flags(no_flags_t) noexcept : bits(VALUE_TYPE{0}) {}
97
99 [[nodiscard]]
100 constexpr bool is_set(enum_type flag) const noexcept { return (bits & flag_bits<VALUE_TYPE>(flag)) != 0; }
101
103 [[nodiscard]]
104 constexpr bool contain(enum_type flag) const noexcept { return this->is_set(flag); }
106 [[nodiscard]]
107 constexpr bool contains(enum_type flag) const noexcept { return this->is_set(flag); }
108
110 [[nodiscard]]
111 constexpr int count() const noexcept { return std::popcount(bits); }
112
114 [[nodiscard]]
115 constexpr ENUM nth_set(size_t n) const noexcept {
116 auto b = bits;
117 while (n--) { b ^= (VALUE_TYPE{ 1 } << std::countr_zero(b)); }
118 return static_cast<ENUM>(std::countr_zero(b));
119 }
120
122 [[nodiscard]]
123 constexpr ENUM first_set() const noexcept { return static_cast<ENUM>(std::countr_zero(bits)); }
124
126 template <typename T, typename... ARGS>
127 [[nodiscard]]
128 constexpr bool contains_any_of(T arg, ARGS... args) const noexcept
129 {
130 return this->is_set(arg) || (this->is_set(args) || ...);
131 }
132
134 [[nodiscard]]
135 constexpr bool are_any_set() const noexcept { return bits != 0; }
136
138 [[nodiscard]]
139 constexpr bool full() const noexcept { return (~bits) == 0; }
140
143 [[nodiscard]]
144 constexpr bool contains_any_of(self_type other) const noexcept { return other.bits == 0 /* empty set */ || (bits & other.bits) != 0; }
145
147 template <typename... ARGS>
148 [[nodiscard]]
149 constexpr bool contains_all_of(ARGS... args) const noexcept
150 {
151 return (this->is_set(args) && ...);
152 }
153
155 [[nodiscard]]
156 constexpr bool contains_all_of(self_type other) const noexcept { return (bits & other.bits) == other.bits; }
157
158 constexpr explicit operator bool() const noexcept { return bits != 0; }
159
161 [[nodiscard]]
162 constexpr enum_type to_enum_type() const noexcept { return (enum_type)bits; }
163
165 template <std::convertible_to<ENUM>... ARGS>
166 constexpr self_type& set(ARGS... args) noexcept { bits |= flag_bits<VALUE_TYPE>(args...); return *this; }
168 constexpr self_type& set(self_type other) noexcept { bits |= other.bits; return *this; }
169
171
173 template <std::convertible_to<ENUM>... ARGS>
174 constexpr self_type& unset(ARGS... args) noexcept { bits &= ~ flag_bits<VALUE_TYPE>(args...); return *this; }
176 constexpr self_type& unset(self_type other) noexcept { bits &= ~other.bits; return *this; }
177
179
181 template <std::convertible_to<ENUM>... ARGS>
182 constexpr self_type& toggle(ARGS... args) noexcept { bits ^= flag_bits<VALUE_TYPE>(args...); return *this; }
184 constexpr self_type& toggle(self_type other) noexcept { bits ^= other.bits; return *this; }
185
187 template <std::convertible_to<ENUM>... ARGS>
188 constexpr self_type& set_to(bool val, ARGS... args) noexcept
189 {
190 if (val) bits |= flag_bits<VALUE_TYPE>(args...); else bits &= ~flag_bits<VALUE_TYPE>(args...);
191 return *this;
192 }
193
195 constexpr self_type& set_to(bool val, self_type other) noexcept
196 {
197 if (val) bits |= other.bits; else bits &= ~other.bits;
198 return *this;
199 }
200
202 [[nodiscard]]
203 constexpr self_type but_only(self_type flags) const noexcept { return self_type::from_bits(bits & flags.bits); }
204
206 [[nodiscard]]
207 constexpr self_type intersected_with(self_type flags) const noexcept { return self_type::from_bits(bits & flags.bits); }
208
209 [[nodiscard]]
210 constexpr self_type operator+(enum_type flag) const noexcept { return self_type::from_bits(bits | flag_bits<VALUE_TYPE>(flag)); }
211 [[nodiscard]]
212 constexpr self_type operator-(enum_type flag) const noexcept { return self_type::from_bits(bits & ~ flag_bits<VALUE_TYPE>(flag)); }
213
214 [[nodiscard]]
215 constexpr self_type operator+(self_type flags) const noexcept { return self_type::from_bits(bits | flags.bits); }
216 [[nodiscard]]
217 constexpr self_type operator-(self_type flags) const noexcept { return self_type::from_bits(bits & ~flags.bits); }
218 [[nodiscard]]
219 constexpr self_type except_for(self_type flags) const noexcept { return self_type::from_bits(bits & ~flags.bits); }
220 [[nodiscard]]
221 constexpr self_type without(self_type flags) const noexcept { return self_type::from_bits(bits & ~flags.bits); }
222
223 template <std::convertible_to<ENUM>... ARGS>
224 [[nodiscard]] constexpr self_type except_for(ARGS... args) const noexcept { return self_type::from_bits(bits & ~flag_bits<VALUE_TYPE>(args...)); }
225 template <std::convertible_to<ENUM>... ARGS>
226 [[nodiscard]] constexpr self_type without(ARGS... args) const noexcept { return self_type::from_bits(bits & ~flag_bits<VALUE_TYPE>(args...)); }
227
228 constexpr self_type& operator+=(enum_type flag) noexcept { bits |= flag_bits<VALUE_TYPE>(flag); return *this; }
229 constexpr self_type& operator-=(enum_type flag) noexcept { bits &= ~ flag_bits<VALUE_TYPE>(flag); return *this; }
230
231 constexpr self_type& operator+=(self_type flags) noexcept { bits |= flags.bits; return *this; }
232 constexpr self_type& operator-=(self_type flags) noexcept { bits &= ~flags.bits; return *this; }
233
234 constexpr auto operator<=>(enum_flags const& other) const noexcept = default;
235
236 struct iterator
237 {
239 using bitset_type = std::make_unsigned_t<self_type::value_type>;
240
241 using difference_type = int;
242 using value_type = enum_type;
243 using pointer_type = void;
244 using reference = void;
245 using iterator_category = std::forward_iterator_tag;
246
247
248 enum_type operator*() const noexcept
249 {
250 const auto bitset = static_cast<bitset_type>(bits);
251#pragma warning(suppress: 4146)
252 return static_cast<enum_type>(std::countr_zero(static_cast<bitset_type>(bitset & -bitset)));
253 }
254
255 iterator& operator++() noexcept
256 {
257 const auto bitset = static_cast<bitset_type>(bits);
258#pragma warning(suppress: 4146)
259 const auto t = static_cast<bitset_type>(bitset & -bitset);
260 bits = static_cast<self_type::value_type>(bitset ^ t);
261 return *this;
262 }
263
264 iterator operator++(int) noexcept { const auto result = *this; operator++(); return result; }
265
266 auto operator<=>(iterator const& other) const noexcept = default;
267 };
268
269 iterator begin() const noexcept { return iterator{ bits }; }
270 iterator end() const noexcept { return iterator{ {} }; }
271 iterator cbegin() const noexcept { return iterator{ bits }; }
272 iterator cend() const noexcept { return iterator{ {} }; }
273
274 constexpr bool empty() const noexcept { return bits == 0; }
275 constexpr void clear() noexcept { bits = 0; }
276
283 template <typename FUNC>
284 constexpr auto for_each(FUNC&& callback) const
285 {
286 using return_type = std::invoke_result_t<FUNC, enum_type>;
287 using bitset_type = std::make_unsigned_t<value_type>;
288 auto bitset = static_cast<bitset_type>(bits);
289 while (bitset)
290 {
291#pragma warning(suppress: 4146)
292 const auto t = static_cast<bitset_type>(bitset & -bitset);
293 const auto r = std::countr_zero(t);
294 if constexpr (std::is_convertible_v<return_type, bool>)
295 {
296 if (auto ret = callback((enum_type)r)) return ret;
297 }
298 else
300 bitset ^= t;
301 }
302 if constexpr (std::is_convertible_v<return_type, bool>)
303 return return_type{};
304 }
305
306 using flag_changes = enum_flag_changes<ENUM, VALUE_TYPE>;
307
308 constexpr void apply(flag_changes changes) noexcept
309 {
310 const auto bits_to_xor = changes.bits[0].bits & changes.bits[1].bits;
311 bits ^= bits_to_xor;
312
314 changes.bits[0].bits &= ~bits_to_xor;
315 changes.bits[1].bits &= ~bits_to_xor;
316
318 bits &= ~changes.bits[1].bits;
319 bits |= changes.bits[0].bits;
320 }
321
322 constexpr self_type operator+(flag_changes changes) const noexcept
323 {
324 auto result = *this;
325 result.apply(changes);
326 return result;
327 }
328 };
329
330 template <typename TYPE>
331 struct is_enum_flags : std::false_type {};
332
333 template <typename ENUM, typename VALUE_TYPE>
334 struct is_enum_flags<enum_flags<ENUM, VALUE_TYPE>> : std::true_type {};
335
336 template <typename TYPE>
337 constexpr bool is_enum_flags_v = is_enum_flags<TYPE>::value;
338
339 enum class enum_flag_change : uint8_t
340 {
341 no_change,
342 set,
343 unset,
344 toggle,
345 };
346
347 template <integral_or_enum ENUM, detail::valid_integral VALUE_TYPE>
348 struct enum_flag_changes
349 {
351 using value_type = VALUE_TYPE;
352
354 using enum_type = ENUM;
355 using self_type = enum_flag_changes;
356
357 using flags_type = enum_flags<ENUM, VALUE_TYPE>;
358
359 constexpr enum_flag_changes() noexcept = default;
360 constexpr enum_flag_changes(enum_flag_changes const&) noexcept = default;
361 constexpr enum_flag_changes(enum_flag_changes&&) noexcept = default;
362 constexpr enum_flag_changes& operator=(enum_flag_changes const&) noexcept = default;
363 constexpr enum_flag_changes& operator=(enum_flag_changes&&) noexcept = default;
364
365 static constexpr self_type no_changes() noexcept { return self_type{}; }
366
367 template <std::convertible_to<ENUM>... ARGS>
368 static constexpr self_type to_set(ARGS... args) noexcept { return self_type{ flag_bits<VALUE_TYPE>(args...), 0 }; }
369 static constexpr self_type to_set(flags_type other) noexcept { return self_type{ other, 0 }; }
370 template <std::convertible_to<ENUM>... ARGS>
371 static constexpr self_type to_unset(ARGS... args) noexcept { return self_type{ 0, flag_bits<VALUE_TYPE>(args...) }; }
372 static constexpr self_type to_unset(flags_type other) noexcept { return self_type{ 0, other }; }
373 template <std::convertible_to<ENUM>... ARGS>
374 static constexpr self_type to_toggle(ARGS... args) noexcept { return self_type{ flag_bits<VALUE_TYPE>(args...), flag_bits<VALUE_TYPE>(args...) }; }
375 static constexpr self_type to_toggle(flags_type other) noexcept { return self_type{ other, other }; }
376
377 template <std::convertible_to<ENUM>... ARGS>
378 constexpr self_type& set_change_of(enum_flag_change change, ARGS... args) noexcept
379 {
380 switch (change)
381 {
382 case enum_flag_change::no_change: return dont_change(args...);
383 case enum_flag_change::set: return set(args...);
384 case enum_flag_change::unset: return unset(args...);
385 case enum_flag_change::toggle: return toggle(args...);
386 }
387#if defined(__cpp_lib_unreachable) && __cpp_lib_unreachable >= 202202L
388 std::unreachable();
389#endif
390 }
391
392 constexpr enum_flag_change change_of(enum_type flag) const noexcept
393 {
394 return static_cast<enum_flag_change>(bits[0].is_set(flag) * 1 + bits[1].is_set(flag) * 2);
395 }
396
397 template <std::convertible_to<ENUM>... ARGS>
398 constexpr self_type& set(ARGS... args) noexcept { bits[0].set(args...); bits[1].unset(args...); return *this; }
399 constexpr self_type& set(flags_type other) noexcept { bits[0].set(other); bits[1].unset(other); return *this; }
400
401 template <std::convertible_to<ENUM>... ARGS>
402 constexpr self_type& unset(ARGS... args) noexcept { bits[0].unset(args...); bits[1].set(args...); return *this; }
403 constexpr self_type& unset(flags_type other) noexcept { bits[0].unset(other); bits[1].set(other); return *this; }
404
405 template <std::convertible_to<ENUM>... ARGS>
406 constexpr self_type& toggle(ARGS... args) noexcept { bits[0].set(args...); bits[1].set(args...); return *this; }
407 constexpr self_type& toggle(flags_type other) noexcept { bits[0].set(other); bits[1].set(other); return *this; }
408
409 template <std::convertible_to<ENUM>... ARGS>
410 constexpr self_type& dont_change(ARGS... args) noexcept { bits[0].unset(args...); bits[1].unset(args...); return *this; }
411 constexpr self_type& dont_change(flags_type other) noexcept { bits[0].unset(other); bits[1].unset(other); return *this; }
412
413 constexpr self_type& dont_change_any() noexcept { bits[0].clear(); bits[1].clear(); return *this; }
414
415 constexpr auto operator<=>(self_type const& other) const noexcept = default;
416
417 flags_type bits[2]{ {}, {} };
418
419 private:
420
421 constexpr enum_flag_changes(flags_type b0, flags_type b1) noexcept
422 : bits(b0, b1)
423 {
424 }
425
426 };
427
428}
constexpr auto bit_count
Equal to the number of bits in the type.
Definition bits.h:33
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:55
Primary namespace for everything in this library.
Definition align+rec2.h:10
A (constexpr) value struct that represents a set of bits mapped to an enum (implemented as a bitset)
Definition enum_flags.h:29
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:203
constexpr self_type & unset(self_type other) noexcept
Unsets the flags in the other set.
Definition enum_flags.h:176
static constexpr self_type none() noexcept
Returns a value with none of the bits set.
Definition enum_flags.h:94
EF_NODISCARD static EF_CONSTEXPR self_type from_bits(value_type val) EF_NOEXCEPT
Creates the enum_flags set from the given underlying bits.
Definition enum_flags.h:47
constexpr self_type & set_to(bool val, self_type other) noexcept
Sets the flags in the other set to val
Definition enum_flags.h:195
constexpr bool is_set(enum_type flag) const noexcept
Returns whether or not flag is set.
Definition enum_flags.h:100
static constexpr self_type from_bits(value_type val) noexcept
Creates the enum_flags set from the given underlying bits.
Definition enum_flags.h:76
constexpr self_type & toggle(self_type other) noexcept
Toggles the flags in the other set.
Definition enum_flags.h:184
constexpr self_type & unset(ARGS... args) noexcept
TODO: insert() as an alias for set()
Definition enum_flags.h:174
constexpr bool contains(enum_type flag) const noexcept
Same as is_set.
Definition enum_flags.h:107
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:156
constexpr bool full() const noexcept
Returns whether *this == all()
Definition enum_flags.h:139
constexpr ENUM 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:123
constexpr bool contains_any_of(T arg, ARGS... args) const noexcept
Returns whether or not any of the given flags are set.
Definition enum_flags.h:128
constexpr self_type & set(ARGS... args) noexcept
Sets the given flags.
Definition enum_flags.h:166
constexpr bool contains_all_of(ARGS... args) const noexcept
Returns whether or not all of the given flags are set.
Definition enum_flags.h:149
constexpr ENUM nth_set(size_t n) const noexcept
Returns the value of the nth set bit in the set.
Definition enum_flags.h:115
constexpr enum_flags(enum_type base_value, ARGS... args) noexcept
Constructs the value with all the given flags set.
Definition enum_flags.h:72
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:207
constexpr self_type & toggle(ARGS... args) noexcept
TODO: erase() as an alias for unset()
Definition enum_flags.h:182
constexpr auto for_each(FUNC &&callback) const
Calls callback for each enum flag in the set.
Definition enum_flags.h:284
constexpr self_type & set(self_type other) noexcept
Sets the flags in the other
Definition enum_flags.h:168
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:90
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:144
constexpr bool contain(enum_type flag) const noexcept
Same as is_set.
Definition enum_flags.h:104
constexpr int count() const noexcept
Returns the number of flags set.
Definition enum_flags.h:111
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:84
ENUM_TYPE enum_type
Type of the enum that is the source of the flags.
Definition enum_flags.h:34
EF_NODISCARD EF_CONSTEXPR bool is_set(enum_type flag) const EF_NOEXCEPT
Returns whether or not flag is set.
Definition enum_flags.h:63
constexpr bool are_any_set() const noexcept
Returns whether or not any of the given flags are set.
Definition enum_flags.h:135
constexpr self_type & set_to(bool val, ARGS... args) noexcept
Sets the given flags to val
Definition enum_flags.h:188
VALUE_TYPE value_type
The underlying integral value type that holds the bits representing the flags.
Definition enum_flags.h:31
constexpr enum_type to_enum_type() const noexcept
Returns the underlying value representing this set cast to the enum_type.
Definition enum_flags.h:162