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