|
| 1 | +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. |
| 2 | +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. |
| 3 | +// All rights not expressly granted are reserved. |
| 4 | +// |
| 5 | +// This software is distributed under the terms of the GNU General Public |
| 6 | +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". |
| 7 | +// |
| 8 | +// In applying this license CERN does not waive the privileges and immunities |
| 9 | +// granted to it by virtue of its status as an Intergovernmental Organization |
| 10 | +// or submit itself to any jurisdiction. |
| 11 | +#ifndef O2_FRAMEWORK_FLAGS_H_ |
| 12 | +#define O2_FRAMEWORK_FLAGS_H_ |
| 13 | + |
| 14 | +#include <stdexcept> |
| 15 | +#include <type_traits> |
| 16 | +#include <string> |
| 17 | +#include <sstream> |
| 18 | +#include <limits> |
| 19 | +#include <bitset> |
| 20 | +#include <initializer_list> |
| 21 | +#include <cstdint> |
| 22 | +#include <cstddef> |
| 23 | + |
| 24 | +namespace o2::flags |
| 25 | +{ |
| 26 | + |
| 27 | +template <typename E> |
| 28 | +concept EFlag = requires { |
| 29 | + requires std::is_enum_v<E>; |
| 30 | + requires std::is_unsigned_v<std::underlying_type_t<E>>; |
| 31 | + requires std::is_convertible_v<std::underlying_type_t<E>, size_t>; |
| 32 | +}; |
| 33 | + |
| 34 | +// Promote an enum to a functioning flag class. The underlying type is |
| 35 | +// the one behind the enum, when bitset becomes constexpr and we get |
| 36 | +// reflection one could changes this. |
| 37 | +template <EFlag E> |
| 38 | +class Flags |
| 39 | +{ |
| 40 | + using U = std::underlying_type_t<E>; |
| 41 | + U mBits{0}; |
| 42 | + |
| 43 | + // Converts enum to its underlying type. |
| 44 | + constexpr auto to_underlying(E e) const noexcept |
| 45 | + { |
| 46 | + return static_cast<U>(e); |
| 47 | + } |
| 48 | + |
| 49 | + // Returns the bit representation of a flag. |
| 50 | + constexpr auto to_bit(E e) const noexcept |
| 51 | + { |
| 52 | + return U(1) << to_underlying(e); |
| 53 | + } |
| 54 | + |
| 55 | + public: |
| 56 | + // Default constructor. |
| 57 | + constexpr explicit Flags() = default; |
| 58 | + // Constructor to initialize with a single flag. |
| 59 | + constexpr explicit Flags(E e) : mBits(to_bit(e)) {} |
| 60 | + // Copy constructor. |
| 61 | + constexpr Flags(const Flags&) = default; |
| 62 | + // Move constructor. |
| 63 | + constexpr Flags(Flags&&) = default; |
| 64 | + // Constructor to initialize with the underlyiny type. |
| 65 | + constexpr explicit Flags(U u) : mBits(u) {} |
| 66 | + // Initialize with a list of flags. |
| 67 | + constexpr Flags(std::initializer_list<E> flags) noexcept |
| 68 | + { |
| 69 | + for (E flag : flags) { |
| 70 | + mBits |= to_bit(flag); |
| 71 | + } |
| 72 | + } |
| 73 | + // Destructor. |
| 74 | + constexpr ~Flags() = default; |
| 75 | + |
| 76 | + static constexpr U None{0}; // Represents no flags set. |
| 77 | + static constexpr U All{std::numeric_limits<U>::max()}; // Represents all flags set. |
| 78 | + static constexpr U Digits{std::numeric_limits<U>::digits()}; // Maximum number of bits in the underlying type. |
| 79 | + |
| 80 | + // Sets flags from a string representation. |
| 81 | + void set(const std::string& s, int base = 2) |
| 82 | + { |
| 83 | + if (base == 2) { // check of only 0 and 1 in string |
| 84 | + if (!std::all_of(s.begin(), s.end(), [](char c) { return c == '0' || c == '1'; })) { |
| 85 | + throw std::invalid_argument("Invalid binary string."); |
| 86 | + } |
| 87 | + } |
| 88 | + uint64_t v = std::stoul(s, nullptr, base); |
| 89 | + if (v > std::numeric_limits<U>::max()) { |
| 90 | + throw std::out_of_range("Values exceeds underlying type range."); |
| 91 | + } |
| 92 | + mBits = static_cast<U>(v); |
| 93 | + } |
| 94 | + |
| 95 | + // Returns the raw bitset value. |
| 96 | + constexpr auto value() const noexcept |
| 97 | + { |
| 98 | + return mBits; |
| 99 | + } |
| 100 | + |
| 101 | + // Resets all flags. |
| 102 | + constexpr void reset() noexcept |
| 103 | + { |
| 104 | + mBits = U(0); |
| 105 | + } |
| 106 | + |
| 107 | + // Resets a specific flag. |
| 108 | + template <typename T> |
| 109 | + requires std::is_same_v<T, E> |
| 110 | + constexpr void reset(T t) |
| 111 | + { |
| 112 | + mBits &= ~to_bit(t); |
| 113 | + } |
| 114 | + |
| 115 | + // Tests if a specific flag is set. |
| 116 | + template <typename T> |
| 117 | + requires std::is_same_v<T, E> |
| 118 | + [[nodiscard]] constexpr bool test(T t) const noexcept |
| 119 | + { |
| 120 | + return (mBits & to_bit(t)) != None; |
| 121 | + } |
| 122 | + |
| 123 | + // Toggles a specific flag. |
| 124 | + template <typename T> |
| 125 | + requires std::is_same_v<T, E> |
| 126 | + constexpr void toggle(T t) noexcept |
| 127 | + { |
| 128 | + mBits ^= to_bit(t); |
| 129 | + } |
| 130 | + |
| 131 | + // Checks if any flag is set. |
| 132 | + [[nodiscard]] constexpr bool any() const noexcept |
| 133 | + { |
| 134 | + return mBits != None; |
| 135 | + } |
| 136 | + |
| 137 | + // Returns the bitset as a binary string. |
| 138 | + [[nodiscard]] std::string string() const |
| 139 | + { |
| 140 | + std::ostringstream oss; |
| 141 | + oss << std::bitset<std::numeric_limits<U>::digits>(mBits); |
| 142 | + return oss.str(); |
| 143 | + } |
| 144 | + |
| 145 | + // Checks if any flag is set (Boolean context). |
| 146 | + constexpr explicit operator bool() const noexcept |
| 147 | + { |
| 148 | + return any(); |
| 149 | + } |
| 150 | + |
| 151 | + // Checks if two flag sets are equal. |
| 152 | + constexpr bool operator==(const Flags& o) const noexcept |
| 153 | + { |
| 154 | + return mBits == o.mBits; |
| 155 | + } |
| 156 | + |
| 157 | + // Checks if two flag sets are not equal. |
| 158 | + constexpr bool operator!=(const Flags& o) const noexcept |
| 159 | + { |
| 160 | + return mBits != o.mBits; |
| 161 | + } |
| 162 | + |
| 163 | + // Copy assignment operator |
| 164 | + constexpr Flags& operator=(const Flags& o) = default; |
| 165 | + |
| 166 | + // Move assignment operator |
| 167 | + constexpr Flags& operator=(Flags&& o) = default; |
| 168 | + |
| 169 | + // Performs a bitwise OR with a flag. |
| 170 | + template <typename T> |
| 171 | + requires std::is_same_v<T, E> |
| 172 | + constexpr Flags& operator|=(T t) noexcept |
| 173 | + { |
| 174 | + mBits |= to_bit(t); |
| 175 | + return *this; |
| 176 | + } |
| 177 | + |
| 178 | + // Performs a bitwise AND with a flag. |
| 179 | + template <typename T> |
| 180 | + requires std::is_same_v<T, E> |
| 181 | + constexpr Flags& operator&=(T t) noexcept |
| 182 | + { |
| 183 | + mBits &= to_bit(t); |
| 184 | + return *this; |
| 185 | + } |
| 186 | + |
| 187 | + // Returns a flag set with a bitwise AND. |
| 188 | + template <typename T> |
| 189 | + requires std::is_same_v<T, E> |
| 190 | + constexpr Flags operator&(T t) const noexcept |
| 191 | + { |
| 192 | + return Flags(mBits & to_bit(t)); |
| 193 | + } |
| 194 | + |
| 195 | + // Returns a flag set with all bits inverted. |
| 196 | + constexpr Flags operator~() const noexcept |
| 197 | + { |
| 198 | + return Flags(~mBits); |
| 199 | + } |
| 200 | + |
| 201 | + // Performs a bitwise OR with another flag set. |
| 202 | + constexpr Flags operator|(const Flags& o) const noexcept |
| 203 | + { |
| 204 | + return Flags(mBits | o.mBits); |
| 205 | + } |
| 206 | + |
| 207 | + // Performs a bitwise OR assignment. |
| 208 | + constexpr Flags& operator|=(const Flags& o) noexcept |
| 209 | + { |
| 210 | + mBits |= o.mBits; |
| 211 | + return *this; |
| 212 | + } |
| 213 | + |
| 214 | + // Performs a bitwise XOR with another flag set. |
| 215 | + constexpr Flags operator^(const Flags& o) const noexcept |
| 216 | + { |
| 217 | + return Flags(mBits ^ o.mBits); |
| 218 | + } |
| 219 | + |
| 220 | + // Performs a bitwise XOR assignment. |
| 221 | + constexpr Flags& operator^=(const Flags& o) noexcept |
| 222 | + { |
| 223 | + mBits ^= o.mBits; |
| 224 | + return *this; |
| 225 | + } |
| 226 | + |
| 227 | + // Checks if all specified flags are set. |
| 228 | + template <typename... Ts> |
| 229 | + constexpr bool all_of(Ts... flags) const noexcept |
| 230 | + { |
| 231 | + return ((test(flags) && ...)); |
| 232 | + } |
| 233 | + |
| 234 | + // Checks if none of the specified flags are set. |
| 235 | + template <typename... Ts> |
| 236 | + constexpr bool none_of(Ts... flags) const noexcept |
| 237 | + { |
| 238 | + return (!(test(flags) || ...)); |
| 239 | + } |
| 240 | + |
| 241 | + // Serializes the flag set to a string. |
| 242 | + [[nodiscard]] std::string serialize() const |
| 243 | + { |
| 244 | + return std::to_string(mBits); |
| 245 | + } |
| 246 | + |
| 247 | + // Deserializes a string into the flag set. |
| 248 | + void deserialize(const std::string& data) |
| 249 | + { |
| 250 | + uint64_t v = std::stoul(data); |
| 251 | + if (v > std::numeric_limits<U>::max()) { |
| 252 | + throw std::out_of_range("Values exceeds underlying type range."); |
| 253 | + } |
| 254 | + mBits = static_cast<U>(v); |
| 255 | + } |
| 256 | + |
| 257 | + // Counts the number of set bits (active flags). |
| 258 | + [[nodiscard]] constexpr size_t count() const noexcept |
| 259 | + { |
| 260 | + return std::bitset<Digits>(mBits).count(); |
| 261 | + } |
| 262 | + |
| 263 | + // Returns the union of two flag sets. |
| 264 | + constexpr Flags union_with(const Flags& o) const noexcept |
| 265 | + { |
| 266 | + return Flags(mBits | o.mBits); |
| 267 | + } |
| 268 | + |
| 269 | + // Returns the intersection of two flag sets. |
| 270 | + constexpr Flags intersection_with(const Flags& o) const noexcept |
| 271 | + { |
| 272 | + return Flags(mBits & o.mBits); |
| 273 | + } |
| 274 | + |
| 275 | + // Checks if all flags in another Flags object are present in the current object. |
| 276 | + constexpr bool contains(const Flags& other) const noexcept |
| 277 | + { |
| 278 | + return (mBits & other.mBits) == other.mBits; |
| 279 | + } |
| 280 | +}; |
| 281 | + |
| 282 | +} // namespace o2::flags |
| 283 | + |
| 284 | +#endif |
0 commit comments