1 // Copyright (C) 2016-2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
2 // This file is subject to the license terms in the LICENSE file
3 // found in the top-level directory of this distribution.
4 
5 #ifndef TYPE_SAFE_FLAG_SET_HPP_INCLUDED
6 #define TYPE_SAFE_FLAG_SET_HPP_INCLUDED
7 
8 #include <climits>
9 #include <cstdint>
10 #include <type_traits>
11 
12 #include <type_safe/flag.hpp>
13 #include <type_safe/types.hpp>
14 
15 namespace type_safe
16 {
17 /// \exclude
18 namespace detail
19 {
20     template <typename Enum, typename = void>
21     struct is_flag_set : std::false_type
22     {};
23 
24     template <typename Enum>
25     struct is_flag_set<Enum, decltype(static_cast<void>(Enum::_flag_set_size))> : std::is_enum<Enum>
26     {};
27 
28     template <typename Enum>
29     constexpr typename std::enable_if<is_flag_set<Enum>::value, std::size_t>::type
flag_set_size()30         flag_set_size() noexcept
31     {
32         return static_cast<std::size_t>(Enum::_flag_set_size);
33     }
34 
35     template <typename Enum>
36     constexpr typename std::enable_if<!is_flag_set<Enum>::value, std::size_t>::type
flag_set_size()37         flag_set_size() noexcept
38     {
39         return 0u;
40     }
41 } // namespace detail
42 
43 /// Traits for the enum used in a [ts::flag_set]().
44 ///
45 /// For each enum that should be used with [ts::flag_set]() it must provide the following interface:
46 /// * Inherit from [std::true_type]().
47 /// * `static constexpr std::size_t size()` that returns the number of enumerators.
48 ///
49 /// The default specialization automatically works for enums that have an enumerator
50 /// `_flag_set_size`, whose value is the number of enumerators. But you can also specialize the
51 /// traits for your own enums. Enums which work with [ts::flag_set]() are called flags.
52 ///
53 /// \requires For all specializations the enum must be contiguous starting at `0`,
54 /// simply don't set an explicit value to the enumerators.
55 template <typename Enum>
56 struct flag_set_traits : detail::is_flag_set<Enum>
57 {
sizetype_safe::flag_set_traits58     static constexpr std::size_t size() noexcept
59     {
60         return detail::flag_set_size<Enum>();
61     }
62 };
63 
64 /// Tag type to mark a [ts::flag_set]() without any flags set.
65 struct noflag_t
66 {
noflag_ttype_safe::noflag_t67     constexpr noflag_t() {}
68 };
69 
70 /// Tag object of type [ts::noflag_t]().
71 constexpr noflag_t noflag;
72 
73 /// \exclude
74 namespace detail
75 {
76     template <std::size_t Size, typename = void>
77     struct select_flag_set_int
78     {
79         static_assert(Size != 0u,
80                       "number of bits not supported, complain loud enough so I'll do it");
81     };
82 
83 /// \exclude
84 #define TYPE_SAFE_DETAIL_SELECT(Min, Max, Type)                                                    \
85     template <std::size_t Size>                                                                    \
86     struct select_flag_set_int<Size, typename std::enable_if<(Size > Min && Size <= Max)>::type>   \
87     {                                                                                              \
88         using type = Type;                                                                         \
89     };
90 
91     TYPE_SAFE_DETAIL_SELECT(0u, 8u, std::uint_least8_t)
92     TYPE_SAFE_DETAIL_SELECT(8u, 16u, std::uint_least16_t)
93     TYPE_SAFE_DETAIL_SELECT(16u, 32u, std::uint_least32_t)
94     TYPE_SAFE_DETAIL_SELECT(32u, sizeof(std::uint_least64_t) * CHAR_BIT, std::uint_least64_t)
95 
96 #undef TYPE_SAFE_DETAIL_SELECT
97 
98     template <typename Enum, typename Tag = void>
99     class flag_set_impl
100     {
101     public:
102         using traits   = flag_set_traits<Enum>;
103         using int_type = typename select_flag_set_int<traits::size()>::type;
104 
all_set()105         static constexpr flag_set_impl all_set()
106         {
107             return flag_set_impl(int_type((int_type(1) << traits::size()) - int_type(1)));
108         }
none_set()109         static constexpr flag_set_impl none_set()
110         {
111             return flag_set_impl(int_type(0));
112         }
113 
flag_set_impl(const Enum & e)114         explicit constexpr flag_set_impl(const Enum& e) : bits_(mask(e)) {}
115         template <typename Tag2>
flag_set_impl(const flag_set_impl<Enum,Tag2> & other)116         explicit constexpr flag_set_impl(const flag_set_impl<Enum, Tag2>& other)
117         : bits_(other.bits_)
118         {}
119 
set(const Enum & e) const120         constexpr flag_set_impl set(const Enum& e) const
121         {
122             return flag_set_impl(bits_ | mask(e));
123         }
reset(const Enum & e) const124         constexpr flag_set_impl reset(const Enum& e) const
125         {
126             return flag_set_impl(bits_ & ~mask(e));
127         }
toggle(const Enum & e) const128         constexpr flag_set_impl toggle(const Enum& e) const
129         {
130             return flag_set_impl(bits_ ^ mask(e));
131         }
132 
toggle_all() const133         constexpr flag_set_impl toggle_all() const
134         {
135             return flag_set_impl(bits_ ^ all_set().to_int());
136         }
137 
to_int() const138         constexpr int_type to_int() const
139         {
140             return bits_;
141         }
142 
is_set(const Enum & e) const143         constexpr bool is_set(const Enum& e) const
144         {
145             return (bits_ & mask(e)) != int_type(0u);
146         }
147 
bitwise_or(const flag_set_impl & other) const148         constexpr flag_set_impl bitwise_or(const flag_set_impl& other) const
149         {
150             return flag_set_impl(bits_ | other.bits_);
151         }
152 
bitwise_xor(const flag_set_impl & other) const153         constexpr flag_set_impl bitwise_xor(const flag_set_impl& other) const
154         {
155             return flag_set_impl(bits_ ^ other.bits_);
156         }
157 
bitwise_and(const flag_set_impl & other) const158         constexpr flag_set_impl bitwise_and(const flag_set_impl& other) const
159         {
160             return flag_set_impl(bits_ & other.bits_);
161         }
162 
163     private:
mask(const Enum & e)164         static constexpr int_type mask(const Enum& e)
165         {
166             return int_type(int_type(1u) << static_cast<std::size_t>(e));
167         }
168 
169         template <typename T, typename = typename std::enable_if<std::is_integral<T>::value
170                                                                  && std::is_unsigned<T>::value>>
flag_set_impl(T bits)171         explicit constexpr flag_set_impl(T bits) : bits_(int_type(bits))
172         {}
173 
174         int_type bits_;
175 
176         template <typename Enum2, typename Tag2>
177         friend class flag_set_impl;
178     };
179 
180     template <typename Enum>
181     using flag_combo = flag_set_impl<Enum, struct combo_tag>;
182 
183     template <typename Enum>
184     using flag_mask = flag_set_impl<Enum, struct mask_tag>;
185 
186     template <typename Enum>
operator ==(const flag_combo<Enum> & a,noflag_t)187     constexpr bool operator==(const flag_combo<Enum>& a, noflag_t)
188     {
189         return a == a.none_set();
190     }
191     template <typename Enum>
operator ==(noflag_t,const flag_combo<Enum> & a)192     constexpr bool operator==(noflag_t, const flag_combo<Enum>& a)
193     {
194         return a == a.none_set();
195     }
196     template <typename Enum>
operator ==(const flag_combo<Enum> & a,const flag_combo<Enum> & b)197     constexpr bool operator==(const flag_combo<Enum>& a, const flag_combo<Enum>& b)
198     {
199         return a.to_int() == b.to_int();
200     }
201     template <typename Enum>
operator ==(const flag_combo<Enum> & a,const Enum & b)202     constexpr bool operator==(const flag_combo<Enum>& a, const Enum& b)
203     {
204         return a == flag_combo<Enum>(b);
205     }
206     template <typename Enum>
operator ==(const Enum & a,const flag_combo<Enum> & b)207     constexpr bool operator==(const Enum& a, const flag_combo<Enum>& b)
208     {
209         return flag_combo<Enum>(a) == b;
210     }
211 
212     template <typename Enum>
operator !=(const flag_combo<Enum> & a,noflag_t b)213     constexpr bool operator!=(const flag_combo<Enum>& a, noflag_t b)
214     {
215         return !(a == b);
216     }
217     template <typename Enum>
operator !=(noflag_t a,const flag_combo<Enum> & b)218     constexpr bool operator!=(noflag_t a, const flag_combo<Enum>& b)
219     {
220         return !(a == b);
221     }
222     template <typename Enum>
operator !=(const flag_combo<Enum> & a,const flag_combo<Enum> & b)223     constexpr bool operator!=(const flag_combo<Enum>& a, const flag_combo<Enum>& b)
224     {
225         return !(a == b);
226     }
227     template <typename Enum>
operator !=(const flag_combo<Enum> & a,const Enum & b)228     constexpr bool operator!=(const flag_combo<Enum>& a, const Enum& b)
229     {
230         return !(a == b);
231     }
232     template <typename Enum>
operator !=(const Enum & a,const flag_combo<Enum> & b)233     constexpr bool operator!=(const Enum& a, const flag_combo<Enum>& b)
234     {
235         return !(a == b);
236     }
237 
238     template <typename Enum>
operator |(const flag_combo<Enum> & a,const flag_combo<Enum> & b)239     constexpr flag_combo<Enum> operator|(const flag_combo<Enum>& a, const flag_combo<Enum>& b)
240     {
241         return a.bitwise_or(b);
242     }
243     template <typename Enum>
operator |(const flag_combo<Enum> & a,const Enum & b)244     constexpr flag_combo<Enum> operator|(const flag_combo<Enum>& a, const Enum& b)
245     {
246         return a | flag_combo<Enum>(b);
247     }
248     template <typename Enum>
operator |(const Enum & a,const flag_combo<Enum> & b)249     constexpr flag_combo<Enum> operator|(const Enum& a, const flag_combo<Enum>& b)
250     {
251         return flag_combo<Enum>(a) | b;
252     }
253 
254     template <typename Enum>
operator ==(const flag_mask<Enum> & a,const flag_mask<Enum> & b)255     constexpr bool operator==(const flag_mask<Enum>& a, const flag_mask<Enum>& b)
256     {
257         return a.to_int() == b.to_int();
258     }
259     template <typename Enum>
operator ==(const flag_mask<Enum> & a,noflag_t)260     constexpr bool operator==(const flag_mask<Enum>& a, noflag_t)
261     {
262         return a == a.none_set();
263     }
264     template <typename Enum>
operator ==(noflag_t,const flag_mask<Enum> & a)265     constexpr bool operator==(noflag_t, const flag_mask<Enum>& a)
266     {
267         return a == a.none_set();
268     }
269     template <typename Enum>
operator !=(const flag_mask<Enum> & a,const flag_mask<Enum> & b)270     constexpr bool operator!=(const flag_mask<Enum>& a, const flag_mask<Enum>& b)
271     {
272         return !(a == b);
273     }
274     template <typename Enum>
operator !=(const flag_mask<Enum> & a,noflag_t b)275     constexpr bool operator!=(const flag_mask<Enum>& a, noflag_t b)
276     {
277         return !(a == b);
278     }
279     template <typename Enum>
operator !=(noflag_t a,const flag_mask<Enum> & b)280     constexpr bool operator!=(noflag_t a, const flag_mask<Enum>& b)
281     {
282         return !(a == b);
283     }
284 
285     template <typename Enum>
operator &(const flag_mask<Enum> & a,const flag_mask<Enum> & b)286     constexpr flag_mask<Enum> operator&(const flag_mask<Enum>& a, const flag_mask<Enum>& b)
287     {
288         return a.bitwise_and(b);
289     }
290 
291     template <typename Enum>
operator ~(const flag_combo<Enum> & a)292     constexpr flag_mask<Enum> operator~(const flag_combo<Enum>& a)
293     {
294         return flag_mask<Enum>(a.toggle_all());
295     }
296     template <typename Enum>
operator ~(const flag_mask<Enum> & a)297     constexpr flag_combo<Enum> operator~(const flag_mask<Enum>& a)
298     {
299         return flag_combo<Enum>(a.toggle_all());
300     }
301 
302     template <typename T, typename Enum>
303     struct is_flag_combo : std::false_type
304     {};
305 
306     template <typename Enum>
307     struct is_flag_combo<Enum, Enum> : flag_set_traits<Enum>
308     {};
309 
310     template <typename Enum>
311     struct is_flag_combo<flag_combo<Enum>, Enum> : flag_set_traits<Enum>
312     {};
313 
314     template <typename T>
315     using enable_flag = typename std::enable_if<flag_set_traits<T>::value>::type;
316 
317     template <typename T, typename Enum>
318     using enable_flag_combo = typename std::enable_if<is_flag_combo<T, Enum>::value>::type;
319 
320     struct get_flag_set_impl
321     {
322         template <class Set>
gettype_safe::detail::get_flag_set_impl323         static constexpr auto get(const Set& set) -> decltype(set.flags_)
324         {
325             return set.flags_;
326         }
327     };
328 } // namespace detail
329 
330 /// Represents a combination of multiple flags.
331 ///
332 /// This type is created when you write `a | b`,
333 /// where `a` and `b` are enumerators of a flag.
334 ///
335 /// Objects of this type and the flags themselves
336 /// are flag combinations.
337 /// You can compare two flag combinations,
338 /// combine two with `|`
339 /// and use them in [ts::flag_set]() to set or toggle a combination of flags.
340 /// Creating the complement with `~` will create a [ts::flag_mask]().
341 ///
342 /// \requires `Enum` must be a flag,
343 /// i.e. valid with the [ts::flag_set_traits]().
344 template <typename Enum>
345 using flag_combo = detail::flag_combo<Enum>;
346 
347 /// Represents a mask of flags.
348 ///
349 /// This type is created when you write `~a`,
350 /// where `a` is the enumerator of a flag.
351 ///
352 /// Objects of this type are flag masks.
353 /// You can compare two flag masks,
354 /// combine them with `&`
355 /// and use them in [ts::flag_set]() to clear a combination of flags.
356 /// Creating the complement with `~` will create a [ts::flag_combo]().
357 ///
358 /// \requires `Enum` must be a flag,
359 /// i.e. valid with the [ts::flag_set_traits]().
360 template <typename Enum>
361 using flag_mask = detail::flag_mask<Enum>;
362 
363 /// Converts a flag mask to a flag combination.
364 /// \returns The flag combination with the same value as the mask.
365 /// \notes As you cannot use a mask to set flags in a [ts::flag_set](),
366 /// you cannot write `~a` to set all flags except `a` directly,
367 /// you have to be explicit.
368 template <typename Enum>
combo(const flag_mask<Enum> & mask)369 constexpr flag_combo<Enum> combo(const flag_mask<Enum>& mask) noexcept
370 {
371     return flag_combo<Enum>(mask);
372 }
373 
374 /// Converts a flag combination to a flag mask.
375 /// \returns The flag mask with the same value as the flag combination.
376 /// \notes (1) does not participate in overload resolution,
377 /// unless the argument is a flagg.
378 /// \notes As you cannot use a combination to clear flags in a [ts::flag_set](),
379 /// you cannot write `a` to clear all flags except `a` directly,
380 /// you have to be explicit.
381 /// \group mask_combo
382 /// \param 1
383 /// \exclude
384 template <typename Enum, typename = detail::enable_flag<Enum>>
mask(const Enum & flag)385 constexpr flag_mask<Enum> mask(const Enum& flag) noexcept
386 {
387     return flag_mask<Enum>(flag);
388 }
389 
390 /// \group mask_combo
391 template <typename Enum>
mask(const flag_combo<Enum> & combo)392 constexpr flag_mask<Enum> mask(const flag_combo<Enum>& combo) noexcept
393 {
394     return flag_mask<Enum>(combo);
395 }
396 
397 /// A set of flags where each one can either be `0` or `1`.
398 ///
399 /// Each enumeration member represents the index of one bit.
400 ///
401 /// Unlike [ts::flag_combo]() or [ts::flag_mask]() this does not have this semantic distinction.
402 /// It is just a generic container of flags,
403 /// which can be set, cleared or toggled.
404 /// It can be interpreted as either a flag combination or flag mask, however.
405 ///
406 /// \requires `Enum` must be a flag,
407 /// i.e. valid with the [ts::flag_set_traits]().
408 template <typename Enum>
409 class flag_set
410 {
411     static_assert(std::is_enum<Enum>::value, "not an enum");
412     static_assert(flag_set_traits<Enum>::value, "invalid enum for flag_set");
413 
414 public:
415     //=== constructors/assignment ===//
416     /// \effects Creates a set where all flags are set to `0`.
417     /// \group ctor_null
flag_set()418     constexpr flag_set() noexcept : flags_(detail::flag_set_impl<Enum>::none_set()) {}
419 
420     /// \group ctor_null
flag_set(noflag_t)421     constexpr flag_set(noflag_t) noexcept : flag_set() {}
422 
423     /// \effects Creates a set where all bits are set to `0` except the given ones.
424     /// \notes This constructor only participates in overload resolution
425     /// if the argument is a flag combination.
426     template <typename FlagCombo, typename = detail::enable_flag_combo<FlagCombo, Enum>>
flag_set(const FlagCombo & combo)427     constexpr flag_set(const FlagCombo& combo) noexcept : flags_(combo)
428     {}
429 
430     /// \effects Same as `*this = flag_set(combo)`.
431     template <typename FlagCombo, typename = detail::enable_flag_combo<FlagCombo, Enum>>
operator =(const FlagCombo & combo)432     flag_set& operator=(const FlagCombo& combo) noexcept
433     {
434         return *this = flag_set(combo);
435     }
436 
437     /// \effects Same as [*reset_all]().
operator =(noflag_t)438     flag_set& operator=(noflag_t) noexcept
439     {
440         reset_all();
441         return *this;
442     }
443 
444     //=== flag operation ===//
445     /// \effects Sets the specified flag to `1` (1)/`value` (2/3).
446     /// \notes (2) does not participate in overload resolution unless `T` is a boolean-like type.
447     /// \group set
set(const Enum & flag)448     void set(const Enum& flag) noexcept
449     {
450         flags_ = flags_.set(flag);
451     }
452 
453     /// \group set
454     /// \param 1
455     /// \exclude
456     template <typename T, typename = detail::enable_boolean<T>>
set(const Enum & flag,T value)457     void set(const Enum& flag, T value) noexcept
458     {
459         if (value)
460             set(flag);
461         else
462             reset(flag);
463     }
464 
465     /// \group set
set(const Enum & f,flag value)466     void set(const Enum& f, flag value) noexcept
467     {
468         set(f, value == true);
469     }
470 
471     /// \effects Sets the specified flag to `0`.
reset(const Enum & flag)472     void reset(const Enum& flag) noexcept
473     {
474         flags_ = flags_.reset(flag);
475     }
476 
477     /// \effects Toggles the specified flag.
toggle(const Enum & flag)478     void toggle(const Enum& flag) noexcept
479     {
480         flags_ = flags_.toggle(flag);
481     }
482 
483     /// \effects Sets/resets/toggles all flags.
484     /// \group all
set_all()485     void set_all() noexcept
486     {
487         flags_ = flags_.all_set();
488     }
489 
490     /// \group all
491     /// \param 1
492     /// \exclude
493     template <typename T, typename = detail::enable_boolean<T>>
set_all(T value)494     void set_all(T value) noexcept
495     {
496         if (value)
497             set_all();
498         else
499             reset_all();
500     }
501 
502     /// \group all
set_all(flag value)503     void set_all(flag value) noexcept
504     {
505         set_all(value == true);
506     }
507 
508     /// \group all
reset_all()509     void reset_all() noexcept
510     {
511         flags_ = flags_.none_set();
512     }
513 
514     /// \group all
toggle_all()515     void toggle_all() noexcept
516     {
517         flags_ = flags_.toggle_all();
518     }
519 
520     /// \returns Whether or not the specified flag is set.
is_set(const Enum & flag) const521     constexpr bool is_set(const Enum& flag) const noexcept
522     {
523         return flags_.is_set(flag);
524     }
525 
526     /// \returns Same as `flag(is_set(flag))`.
as_flag(const Enum & flag) const527     constexpr flag as_flag(const Enum& flag) const noexcept
528     {
529         return is_set(flag);
530     }
531 
532     //=== accessors ===//
533     /// \returns Whether any flag is set.
any() const534     constexpr bool any() const noexcept
535     {
536         return flags_.to_int() != flags_.none_set().to_int();
537     }
538 
539     /// \returns Whether all flags are set.
all() const540     constexpr bool all() const noexcept
541     {
542         return flags_.to_int() == flags_.all_set().to_int();
543     }
544 
545     /// \returns Whether no flag is set.
none() const546     constexpr bool none() const noexcept
547     {
548         return !any();
549     }
550 
551     /// \returns An integer where each bit has the value of the corresponding flag.
552     /// \requires `T` must be an unsigned integer type with enough bits.
553     template <typename T>
to_int() const554     constexpr T to_int() const noexcept
555     {
556         static_assert(std::is_unsigned<T>::value
557                           && sizeof(T) * CHAR_BIT >= flag_set_traits<Enum>::size(),
558                       "invalid integer type, lossy conversion");
559         return flags_.to_int();
560     }
561 
562     //=== bitwise operations ===//
563     /// \returns A set with all the flags flipped.
operator ~() const564     constexpr flag_set operator~() const noexcept
565     {
566         return flag_set(flag_combo<Enum>(flags_.toggle_all()));
567     }
568 
569     /// \effects Sets all flags that are set in the given flag combination.
570     /// \returns `*this`
571     /// \notes This operator does not participate in overload resolution,
572     /// unless the argument is a flag combination.
573     /// If you truly want to write `set |= ~a`,
574     /// i.e. set all flags except `a`, use `set |= combo(~a)`.
575     template <typename FlagCombo, typename = detail::enable_flag_combo<FlagCombo, Enum>>
operator |=(const FlagCombo & other)576     flag_set& operator|=(const FlagCombo& other) noexcept
577     {
578         flags_ = flags_.bitwise_or(detail::flag_set_impl<Enum>(other));
579         return *this;
580     }
581 
582     /// \effects Toggles all flags that are set in the given flag combination.
583     /// \returns `*this`
584     /// \notes This operator does not participate in overload resolution,
585     /// unless the argument is a flag combination.
586     /// If you truly want to write `set ^= ~a`,
587     /// i.e. toggle all flags except `a`, use `set ^= combo(~a)`.
588     template <typename FlagCombo, typename = detail::enable_flag_combo<FlagCombo, Enum>>
operator ^=(const FlagCombo & other)589     flag_set& operator^=(const FlagCombo& other) noexcept
590     {
591         flags_ = flags_.bitwise_xor(detail::flag_set_impl<Enum>(other));
592         return *this;
593     }
594 
595     /// \effects Clears all flags that aren't set in the given flag mask.
596     /// \returns `*this`
597     /// \notes This operator does not participate in overload resolution,
598     /// unless the argument is a flag mask.
599     /// If you truly want to write `set &= a`,
600     /// i.e. clear all flags except `a`, use `set &= mask(a)`.
operator &=(const flag_mask<Enum> & other)601     flag_set& operator&=(const flag_mask<Enum>& other) noexcept
602     {
603         flags_ = flags_.bitwise_and(detail::flag_set_impl<Enum>(other));
604         return *this;
605     }
606 
607 private:
608     detail::flag_set_impl<Enum> flags_;
609 
610     friend detail::get_flag_set_impl;
611 };
612 
613 /// Converts a [ts::flag_set]() to a flag combination.
614 /// \returns The flag combination with the same value as the set.
615 template <typename Enum>
combo(const flag_set<Enum> & set)616 constexpr flag_combo<Enum> combo(const flag_set<Enum>& set) noexcept
617 {
618     return flag_combo<Enum>(detail::get_flag_set_impl::get(set));
619 }
620 
621 /// Converts a [ts::flag_set]() to a flag mask.
622 /// \returns The flag mask with the same value as the set.
623 template <typename Enum>
mask(const flag_set<Enum> & set)624 constexpr flag_mask<Enum> mask(const flag_set<Enum>& set) noexcept
625 {
626     return flag_mask<Enum>(detail::get_flag_set_impl::get(set));
627 }
628 
629 /// `flag_set` equality comparison.
630 /// \returns Whether both flag sets have the same combination of flags set/not set.
631 /// \group flag_set_equal flag_set equality comparison
632 template <typename Enum>
operator ==(const flag_set<Enum> & a,const flag_set<Enum> & b)633 constexpr bool operator==(const flag_set<Enum>& a, const flag_set<Enum>& b) noexcept
634 {
635     return combo(a) == combo(b);
636 }
637 
638 /// \group flag_set_equal
639 template <typename Enum>
operator ==(const flag_set<Enum> & a,noflag_t b)640 constexpr bool operator==(const flag_set<Enum>& a, noflag_t b) noexcept
641 {
642     return combo(a) == b;
643 }
644 
645 /// \group flag_set_equal
646 template <typename Enum>
operator ==(noflag_t a,const flag_set<Enum> & b)647 constexpr bool operator==(noflag_t a, const flag_set<Enum>& b) noexcept
648 {
649     return a == combo(b);
650 }
651 
652 /// \group flag_set_equal
653 template <typename Enum, typename FlagCombo, typename = detail::enable_flag_combo<FlagCombo, Enum>>
operator ==(const flag_set<Enum> & a,const FlagCombo & b)654 constexpr bool operator==(const flag_set<Enum>& a, const FlagCombo& b) noexcept
655 {
656     return combo(a) == b;
657 }
658 
659 /// \group flag_set_equal
660 template <typename Enum, typename FlagCombo, typename = detail::enable_flag_combo<FlagCombo, Enum>>
operator ==(const FlagCombo & a,const flag_set<Enum> & b)661 constexpr bool operator==(const FlagCombo& a, const flag_set<Enum>& b) noexcept
662 {
663     return a == combo(b);
664 }
665 
666 /// \group flag_set_equal
667 template <typename Enum>
operator !=(const flag_set<Enum> & a,const flag_set<Enum> & b)668 constexpr bool operator!=(const flag_set<Enum>& a, const flag_set<Enum>& b) noexcept
669 {
670     return !(a == b);
671 }
672 
673 /// \group flag_set_equal
674 template <typename Enum>
operator !=(const flag_set<Enum> & a,noflag_t b)675 constexpr bool operator!=(const flag_set<Enum>& a, noflag_t b) noexcept
676 {
677     return !(a == b);
678 }
679 
680 /// \group flag_set_equal
681 template <typename Enum>
operator !=(noflag_t a,const flag_set<Enum> & b)682 constexpr bool operator!=(noflag_t a, const flag_set<Enum>& b) noexcept
683 {
684     return !(a == b);
685 }
686 
687 /// \group flag_set_equal
688 template <typename Enum, typename FlagCombo, typename = detail::enable_flag_combo<FlagCombo, Enum>>
operator !=(const flag_set<Enum> & a,const FlagCombo & b)689 constexpr bool operator!=(const flag_set<Enum>& a, const FlagCombo& b) noexcept
690 {
691     return !(a == b);
692 }
693 
694 /// \group flag_set_equal
695 template <typename Enum, typename FlagCombo, typename = detail::enable_flag_combo<FlagCombo, Enum>>
operator !=(const FlagCombo & a,const flag_set<Enum> & b)696 constexpr bool operator!=(const FlagCombo& a, const flag_set<Enum>& b) noexcept
697 {
698     return !(a == b);
699 }
700 
701 /// \returns The same as `a Op= b`.
702 /// \group bitwise_op Bitwise operations for flag_set
703 /// \param 2
704 /// \exclude
705 template <typename Enum, typename FlagCombo, typename = detail::enable_flag_combo<FlagCombo, Enum>>
operator |(const flag_set<Enum> & a,const FlagCombo & b)706 constexpr flag_set<Enum> operator|(const flag_set<Enum>& a, const FlagCombo& b)
707 {
708     return combo(a).bitwise_or(flag_combo<Enum>(b));
709 }
710 /// \group bitwise_op
711 /// \param 2
712 /// \exclude
713 template <typename Enum, typename FlagCombo, typename = detail::enable_flag_combo<FlagCombo, Enum>>
operator |(const FlagCombo & a,const flag_set<Enum> & b)714 constexpr flag_set<Enum> operator|(const FlagCombo& a, const flag_set<Enum>& b)
715 {
716     return b | a;
717 }
718 /// \group bitwise_op
719 /// \param 2
720 /// \exclude
721 template <typename Enum, typename FlagCombo, typename = detail::enable_flag_combo<FlagCombo, Enum>>
operator ^(const flag_set<Enum> & a,const FlagCombo & b)722 constexpr flag_set<Enum> operator^(const flag_set<Enum>& a, const FlagCombo& b)
723 {
724     return combo(a).bitwise_xor(flag_combo<Enum>(b));
725 }
726 /// \group bitwise_op
727 /// \param 2
728 /// \exclude
729 template <typename Enum, typename FlagCombo, typename = detail::enable_flag_combo<FlagCombo, Enum>>
operator ^(const FlagCombo & a,const flag_set<Enum> & b)730 constexpr flag_set<Enum> operator^(const FlagCombo& a, const flag_set<Enum>& b)
731 {
732     return b ^ a;
733 }
734 /// \group bitwise_op
735 template <typename Enum>
operator &(const flag_set<Enum> & a,const flag_mask<Enum> & b)736 constexpr flag_set<Enum> operator&(const flag_set<Enum>& a, const flag_mask<Enum>& b)
737 {
738     return combo(mask(a).bitwise_and(b));
739 }
740 /// \group bitwise_op
741 template <typename Enum>
operator &(const flag_mask<Enum> & a,const flag_set<Enum> & b)742 constexpr flag_set<Enum> operator&(const flag_mask<Enum>& a, const flag_set<Enum>& b)
743 {
744     return b & a;
745 }
746 
747 /// Checks whether a combination of flags is set in `a`.
748 /// \returns `true` if all the flags set in `b` are also set in `a`,
749 /// `false` otherwise.
750 /// \notes These functions do not participate in overload resolution,
751 /// unless `FlagCombo` is a flag operation.
752 /// \group bitwise_and_check Bitwise and for flag_set
753 /// \param 2
754 /// \exclude
755 template <typename Enum, typename FlagCombo, typename = detail::enable_flag_combo<FlagCombo, Enum>>
operator &(const flag_set<Enum> & a,const FlagCombo & b)756 constexpr bool operator&(const flag_set<Enum>& a, const FlagCombo& b)
757 {
758     return static_cast<bool>(combo(a).bitwise_and(flag_combo<Enum>(b)).to_int());
759 }
760 /// \group bitwise_and_check
761 /// \param 2
762 /// \exclude
763 template <typename Enum, typename FlagCombo, typename = detail::enable_flag_combo<FlagCombo, Enum>>
operator &(const FlagCombo & a,const flag_set<Enum> & b)764 constexpr bool operator&(const FlagCombo& a, const flag_set<Enum>& b)
765 {
766     return b & a;
767 }
768 } // namespace type_safe
769 
770 /// Creates a [ts::flag_mask]() for the single enum value.
771 /// \returns A [ts::flag_mask]() where all bits are set,
772 /// unless the given one.
773 /// \notes This function does not participate in overload resolution,
774 /// unless `Enum` is an `enum` where the [ts::flag_set_traits]() are specialized.
775 /// \param 1
776 /// \exclude
777 template <typename Enum, typename = type_safe::detail::enable_flag<Enum>>
operator ~(const Enum & e)778 constexpr type_safe::flag_mask<Enum> operator~(const Enum& e) noexcept
779 {
780     return type_safe::flag_mask<Enum>::all_set().reset(e);
781 }
782 
783 /// Creates a [ts::flag_combo]() from two enums.
784 /// \returns A [ts::flag_combo]() where the two given bits are set.
785 /// \notes These functions do not participate in overload resolution,
786 /// unless `Enum` is an `enum` where the [ts::flag_set_traits]() are specialized.
787 /// \param 1
788 /// \exclude
789 template <typename Enum, typename = type_safe::detail::enable_flag<Enum>>
operator |(const Enum & a,const Enum & b)790 constexpr type_safe::flag_combo<Enum> operator|(const Enum& a, const Enum& b) noexcept
791 {
792     return type_safe::flag_combo<Enum>(a) | b;
793 }
794 
795 #endif // TYPE_SAFE_FLAG_SET_HPP_INCLUDED
796