Line data Source code
1 : #pragma once
2 :
3 : #include "issm-precice/config.hpp"
4 :
5 : #include <compare>
6 : #include <ostream>
7 : #include <type_traits>
8 : #include <utility>
9 :
10 : #include "fmt/format.h"
11 :
12 : namespace ipc
13 : {
14 :
15 : /**
16 : * Traits class that enables an enum type to be used in the typesafe enum flag.
17 : * Specialize for your enum and derive from std::true_type.
18 : * This enables usage of the enum class as flags using the usual | and & bitwise operators.
19 : * Example:
20 : * ```
21 : * enum class Foo { None = 0, Flag1 = 0B1, Flag2 = 0B10, Flag3 = 0B100, };
22 : * template<> struct EnumFlagTraits<Foo> : std::true_type { }
23 : * FlagType<Foo> flags = Flag1 | Flag3;
24 : * //...
25 : * if (flags & Foo::Flag1) { printf("Flag1 is set."); }
26 : * ```
27 : */
28 : template<class E>
29 : struct EnumFlagTraits : std::false_type
30 : {
31 : };
32 :
33 : /**
34 : * A type safe bitfield based on flags defined by an enum type.
35 : * Ensures that only values of the specified enum type are set.
36 : * @tparam E an enum type whose members are flags.
37 : */
38 : template<class E>
39 : struct FlagType
40 : {
41 : static_assert(std::is_enum_v<E>);
42 :
43 : /**
44 : * The combined values of all flags.
45 : */
46 12 : std::underlying_type_t<E> value;
47 :
48 : /**
49 : * Default constructor.
50 : * No flags are set.
51 : */
52 2 : FlagType() : value(0) { }
53 :
54 : /**
55 : * Constructor from the underlying type of the enum type.
56 : */
57 77 : explicit FlagType(std::underlying_type_t<E> v) : value(v) { }
58 :
59 : /**
60 : * Constructor to set a single flag.
61 : * Implicit conversion from E to FlagType<E>.
62 : */
63 27 : FlagType(E e) : value(std::underlying_type_t<E>(e)) { }
64 :
65 : /**
66 : * Default comparison operator.
67 : * Equal if the same flags are set in both operands.
68 : * Can also be used to compare with single flags due to implicit conversion.
69 : * @{
70 : */
71 12 : bool operator==(const FlagType& b) const = default;
72 : bool operator!=(const FlagType& b) const = default;
73 : /** @} */
74 :
75 : /**
76 : * Check if a specific flag is set.
77 : * `if (f.is_set(a))` is equivalent to `if (f & a)`.
78 : */
79 1 : bool is_set(E e) const { return (*this & e) != 0; }
80 :
81 : /**
82 : * Check if any flag is set.
83 : */
84 44 : bool any() const { return value != 0; }
85 :
86 : /**
87 : * Check if any flag is set.
88 : */
89 42 : operator bool() const { return any(); }
90 :
91 : /**
92 : * Form the union of the flags from two bitfields in place.
93 : */
94 2 : FlagType& operator|=(const FlagType& b) { return (*this = (*this | b)); }
95 :
96 : /**
97 : * Output the underlying flag value.
98 : */
99 : std::ostream& operator<<(std::ostream& os)
100 : {
101 : os << E(value);
102 : return os;
103 : }
104 : };
105 :
106 : /**
107 : * Extension for the fmt library.
108 : */
109 : template<class E, class = std::enable_if_t<EnumFlagTraits<E>::value>>
110 : auto format_as(FlagType<E> flags)
111 : {
112 : return flags.value;
113 : }
114 :
115 : /**
116 : * Union of two flags.
117 : */
118 : template<class E>
119 14 : std::enable_if_t<EnumFlagTraits<E>::value, FlagType<E>> operator|(E a, E b)
120 : {
121 14 : return FlagType<E>{std::underlying_type_t<E>(a) | std::underlying_type_t<E>(b)};
122 : }
123 :
124 : /**
125 : * Union of a flag and an exisiting set.
126 : * @{
127 : */
128 : template<class E>
129 10 : std::enable_if_t<EnumFlagTraits<E>::value, FlagType<E>> operator|(FlagType<E> a, E b)
130 : {
131 10 : return FlagType<E>{a.value | std::underlying_type_t<E>(b)};
132 : }
133 : template<class E>
134 1 : std::enable_if_t<EnumFlagTraits<E>::value, FlagType<E>> operator|(E a, FlagType<E> b)
135 : {
136 1 : return FlagType<E>{std::underlying_type_t<E>(a) | b.value};
137 : }
138 : /** @} */
139 :
140 : /**
141 : * Form the union of the flags from two bitfields.
142 : */
143 : template<class E>
144 3 : std::enable_if_t<EnumFlagTraits<E>::value, FlagType<E>> operator|(FlagType<E> a, FlagType<E> b)
145 : {
146 3 : return FlagType<E>{a.value | b.value};
147 : }
148 :
149 : /**
150 : * Intersection of two flags.
151 : * Use with `FlagType::any` or `FlagType::operator bool` to check if the flag is set.
152 : */
153 : template<class E>
154 : std::enable_if_t<EnumFlagTraits<E>::value, FlagType<E>> operator&(E a, E b)
155 : {
156 : return FlagType<E>{std::underlying_type_t<E>(a) & std::underlying_type_t<E>(b)};
157 : }
158 :
159 : /**
160 : * Intersection of a bitfield and a single flag.
161 : * Use with `FlagType::any` or `FlagType::operator bool` to check if the flag is set.
162 : * @{
163 : */
164 : template<class E>
165 41 : std::enable_if_t<EnumFlagTraits<E>::value, FlagType<E>> operator&(FlagType<E> a, E b)
166 : {
167 41 : return FlagType<E>{a.value & std::underlying_type_t<E>(b)};
168 : }
169 : template<class E>
170 1 : std::enable_if_t<EnumFlagTraits<E>::value, FlagType<E>> operator&(E b, FlagType<E> a)
171 : {
172 1 : return FlagType<E>{a.value & std::underlying_type_t<E>(b)};
173 : }
174 : /** @} */
175 :
176 : /**
177 : * Intersection of two bit fields.
178 : */
179 : template<class E>
180 1 : std::enable_if_t<EnumFlagTraits<E>::value, FlagType<E>> operator&(FlagType<E> a, FlagType<E> b)
181 : {
182 1 : return FlagType<E>{a.value & std::underlying_type_t<E>(b)};
183 : }
184 :
185 : } // namespace ipc
|