Skip to content

Commit ee980ca

Browse files
committed
Common: Add flag helper class
1 parent 5c52a4b commit ee980ca

File tree

3 files changed

+485
-0
lines changed

3 files changed

+485
-0
lines changed

Common/Utils/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ o2_add_test(MemFileHelper
8181
SOURCES test/testMemFileHelper.cxx
8282
PUBLIC_LINK_LIBRARIES O2::CommonUtils)
8383

84+
o2_add_test(Flags
85+
COMPONENT_NAME CommonUtils
86+
LABELS utils
87+
SOURCES test/testFlags.cxx
88+
PUBLIC_LINK_LIBRARIES O2::CommonUtils)
89+
8490
o2_add_executable(treemergertool
8591
COMPONENT_NAME CommonUtils
8692
SOURCES src/TreeMergerTool.cxx
Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
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

Comments
 (0)