Skip to content

Commit 8a828c6

Browse files
committed
Common: Minor cleanup of flag helper class
Signed-off-by: Felix Schlepper <felix.schlepper@cern.ch>
1 parent 98f5ae0 commit 8a828c6

File tree

2 files changed

+67
-32
lines changed

2 files changed

+67
-32
lines changed

Common/Utils/include/CommonUtils/EnumFlags.h

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,8 @@ struct FlagsHelper final {
144144
{
145145
constexpr std::array<bool, sizeof...(I)> valid{isValid<static_cast<E>(MinScan + I)>()...};
146146
constexpr auto count{std::count_if(valid.cbegin(), valid.cend(), [](bool v) noexcept { return v; })};
147-
static_assert(count > 0, "Requiring non-empty enum!");
148-
static_assert(count <= MaxUnderScan, "Underlying type of enum has less digits than given expected!");
147+
static_assert(count > 0, "EnumFlag requires at least one enum value. Check that your enum has consecutive values starting from 0.");
148+
static_assert(count <= MaxUnderScan, "Too many enum values for underlying type. Consider using a larger underlying type or fewer enum values.");
149149
std::array<E, count> values{};
150150
for (size_t idx{}, n{}; n < count; ++idx) {
151151
if (valid[idx]) {
@@ -173,7 +173,7 @@ struct FlagsHelper final {
173173
}
174174
if constexpr (tpeek_v<e>[tp + getSpec<SVal::Start, SType::Enum_t>().size()] == getSpec<SVal::AnonStart, SType::Enum_t>()) {
175175
#if defined __clang__
176-
if constexpr (tpeek_v<e>[tp + getSpec<SVal::Start, SType::enum_t>().size() + 1] == getSpec<SVal::AnonStart, SType::Enum_t>()) {
176+
if constexpr (tpeek_v<e>[tp + getSpec<SVal::Start, SType::Enum_t>().size() + 1] == getSpec<SVal::AnonStart, SType::Enum_t>()) {
177177
return {};
178178
}
179179
#endif
@@ -215,7 +215,7 @@ struct FlagsHelper final {
215215
template <E e>
216216
static constexpr auto getNameValue{getName<e>()};
217217

218-
template <bool with_scope, std::size_t... I>
218+
template <bool with_scope, size_t... I>
219219
static constexpr auto getNames(std::index_sequence<I...> /*unused*/)
220220
{
221221
if constexpr (with_scope) {
@@ -248,7 +248,7 @@ struct FlagsHelper final {
248248

249249
static constexpr std::optional<E> fromString(std::string_view str) noexcept
250250
{
251-
for (std::size_t i{0}; i < count(); ++i) {
251+
for (size_t i{0}; i < count(); ++i) {
252252
if (Names[i] == str || NamesScoped[i] == str) {
253253
return Values[i];
254254
}
@@ -371,13 +371,18 @@ class EnumFlags
371371
constexpr EnumFlags(const EnumFlags&) = default;
372372
// Move constructor.
373373
constexpr EnumFlags(EnumFlags&&) = default;
374-
// Constructor to initialize with the underlyiny type.
374+
// Constructor to initialize with the underlying type.
375375
constexpr explicit EnumFlags(U u) : mBits(u) {}
376376
// Initialize with a list of flags.
377377
constexpr EnumFlags(std::initializer_list<E> flags) noexcept
378378
{
379379
std::for_each(flags.begin(), flags.end(), [this](const E f) noexcept { mBits |= to_bit(f); });
380380
}
381+
// Init from a string.
382+
EnumFlags(const std::string& str)
383+
{
384+
set(str);
385+
}
381386
// Destructor.
382387
constexpr ~EnumFlags() = default;
383388

@@ -415,7 +420,7 @@ class EnumFlags
415420
}
416421
}
417422
// Returns the raw bitset value.
418-
constexpr auto value() const noexcept
423+
[[nodiscard]] constexpr auto value() const noexcept
419424
{
420425
return mBits;
421426
}
@@ -442,6 +447,13 @@ class EnumFlags
442447
return (mBits & to_bit(t)) != None;
443448
}
444449

450+
// Tests if all specified flags are set.
451+
template <typename... Ts>
452+
[[nodiscard]] constexpr bool test(Ts... flags) const noexcept
453+
{
454+
return ((test(flags) && ...));
455+
}
456+
445457
// Sets a specific flag.
446458
template <typename T>
447459
requires std::is_same_v<T, E>
@@ -505,27 +517,27 @@ class EnumFlags
505517
}
506518

507519
// Checks if any flag is set (Boolean context).
508-
constexpr explicit operator bool() const noexcept
520+
[[nodiscard]] constexpr explicit operator bool() const noexcept
509521
{
510522
return any();
511523
}
512524

513525
// Check if given flag is set.
514526
template <typename T>
515527
requires std::is_same_v<T, E>
516-
constexpr bool operator[](const T t) noexcept
528+
[[nodiscard]] constexpr bool operator[](const T t) const noexcept
517529
{
518530
return test(t);
519531
}
520532

521533
// Checks if two flag sets are equal.
522-
constexpr bool operator==(const EnumFlags& o) const noexcept
534+
[[nodiscard]] constexpr bool operator==(const EnumFlags& o) const noexcept
523535
{
524536
return mBits == o.mBits;
525537
}
526538

527539
// Checks if two flag sets are not equal.
528-
constexpr bool operator!=(const EnumFlags& o) const noexcept
540+
[[nodiscard]] constexpr bool operator!=(const EnumFlags& o) const noexcept
529541
{
530542
return mBits != o.mBits;
531543
}
@@ -584,7 +596,13 @@ class EnumFlags
584596
// Performs a bitwise XOR with another flag set.
585597
constexpr EnumFlags operator^(const EnumFlags& o) const noexcept
586598
{
587-
return Flags(mBits ^ o.mBits);
599+
return EnumFlags(mBits ^ o.mBits);
600+
}
601+
602+
// Performs a bitwise and with another flag set.
603+
constexpr EnumFlags operator&(const EnumFlags& o) const noexcept
604+
{
605+
return EnumFlags(mBits & o.mBits);
588606
}
589607

590608
// Performs a bitwise XOR assignment.
@@ -596,14 +614,14 @@ class EnumFlags
596614

597615
// Checks if all specified flags are set.
598616
template <typename... Ts>
599-
constexpr bool all_of(Ts... flags) const noexcept
617+
[[nodiscard]] constexpr bool all_of(Ts... flags) const noexcept
600618
{
601-
return ((test(flags) && ...));
619+
return test(flags...);
602620
}
603621

604622
// Checks if none of the specified flags are set.
605623
template <typename... Ts>
606-
constexpr bool none_of(Ts... flags) const noexcept
624+
[[nodiscard]] constexpr bool none_of(Ts... flags) const noexcept
607625
{
608626
return (!(test(flags) || ...));
609627
}
@@ -627,35 +645,29 @@ class EnumFlags
627645
// Counts the number of set bits (active flags).
628646
[[nodiscard]] constexpr size_t count() const noexcept
629647
{
630-
size_t c{0};
631-
for (size_t i{H::Min_u_v}; i < H::Max_u_v; ++i) {
632-
if ((mBits & (U(1) << i)) != U(0)) {
633-
++c;
634-
}
635-
}
636-
return c;
648+
return std::popcount(mBits);
637649
}
638650

639651
// Returns the union of two flag sets.
640-
constexpr EnumFlags union_with(const EnumFlags& o) const noexcept
652+
[[nodiscard]] constexpr EnumFlags union_with(const EnumFlags& o) const noexcept
641653
{
642654
return EnumFlags(mBits | o.mBits);
643655
}
644656

645657
// Returns the intersection of two flag sets.
646-
constexpr EnumFlags intersection_with(const EnumFlags& o) const noexcept
658+
[[nodiscard]] constexpr EnumFlags intersection_with(const EnumFlags& o) const noexcept
647659
{
648660
return EnumFlags(mBits & o.mBits);
649661
}
650662

651663
// Checks if all flags in another Flags object are present in the current object.
652-
constexpr bool contains(const EnumFlags& other) const noexcept
664+
[[nodiscard]] constexpr bool contains(const EnumFlags& other) const noexcept
653665
{
654666
return (mBits & other.mBits) == other.mBits;
655667
}
656668

657669
private:
658-
// Set implemnetation, bits was zeroed before.
670+
// Set implementation, bits was zeroed before.
659671
void setImpl(const std::string& s, int base = 2)
660672
{
661673
if (std::all_of(s.begin(), s.end(), [](unsigned char c) { return std::isdigit(c); })) {
@@ -669,15 +681,20 @@ class EnumFlags
669681
throw std::out_of_range("Values exceeds enum range.");
670682
}
671683
mBits = static_cast<U>(v);
672-
} else if (std::all_of(s.begin(), s.end(), [](unsigned char c) { return std::isalnum(c) != 0 || c == '|' || c == ' ' || c == ':' || c == ','; })) {
684+
} else if (std::all_of(s.begin(), s.end(), [](unsigned char c) { return std::isalnum(c) != 0 || c == '|' || c == ' ' || c == ':' || c == ',' || c == ';'; })) {
673685
std::string cs{s};
674686
std::transform(cs.begin(), cs.end(), cs.begin(), [](unsigned char c) { return std::tolower(c); });
675687
if (cs == H::All) {
676688
mBits = All;
677689
} else if (cs == H::None) {
678690
mBits = None;
679691
} else {
680-
char token = (s.find(',') != std::string::npos) ? ',' : '|';
692+
// accept as delimiter ' ', '|', ';', ','
693+
char token = ' ';
694+
std::string::size_type pos = s.find_first_of(",|;");
695+
if (pos != std::string::npos) {
696+
token = s[pos];
697+
}
681698
for (const auto& tok : Str::tokenize(s, token)) {
682699
if (auto e = H::fromString(tok)) {
683700
mBits |= to_bit(*e);

Common/Utils/test/testEnumFlags.cxx

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ BOOST_AUTO_TEST_CASE(Flags_test)
181181
BOOST_TEST(flags.test(TestEnum::Bit4));
182182
}
183183

184-
{ // test with different delimiter
184+
{ // test with , delimiter
185185
std::string str = "Bit4,TestEnum::Bit2 , Bit1 ";
186186
flags.set(str);
187187
BOOST_TEST(flags.test(TestEnum::Bit1));
@@ -190,6 +190,15 @@ BOOST_AUTO_TEST_CASE(Flags_test)
190190
BOOST_TEST(flags.test(TestEnum::Bit4));
191191
}
192192

193+
{ // test with ; delimiter
194+
std::string str = "Bit4;TestEnum::Bit2 ; Bit1 ";
195+
flags.set(str);
196+
BOOST_TEST(flags.test(TestEnum::Bit1));
197+
BOOST_TEST(flags.test(TestEnum::Bit2));
198+
BOOST_TEST(!flags.test(TestEnum::Bit3));
199+
BOOST_TEST(flags.test(TestEnum::Bit4));
200+
}
201+
193202
{ // throw test with mixed delimiter
194203
std::string str = "Bit4|TestEnum::Bit2 , Bit1 ";
195204
BOOST_CHECK_THROW(flags.set(str), std::invalid_argument);
@@ -275,6 +284,14 @@ BOOST_AUTO_TEST_CASE(Flags_test)
275284
EFlags flags3{TestEnum::Bit1, TestEnum::Bit2, TestEnum::Bit3};
276285
EFlags flags4{TestEnum::Bit2, TestEnum::Bit3, TestEnum::Bit4};
277286

287+
// test xor
288+
auto flagsXOR = flags3 ^ flags4;
289+
BOOST_CHECK(flagsXOR.test(TestEnum::Bit1, TestEnum::Bit4));
290+
291+
// test and
292+
auto flagsAND = flags3 & flags4;
293+
BOOST_CHECK(flagsAND.test(TestEnum::Bit2, TestEnum::Bit3));
294+
278295
// Perform an intersection operation
279296
EFlags intersectionFlags = flags3.intersection_with(flags4);
280297
BOOST_CHECK(intersectionFlags.test(TestEnum::Bit2));
@@ -300,8 +317,9 @@ BOOST_AUTO_TEST_CASE(Flags_test)
300317

301318
{
302319
// Test compilation using an enum with more than 32 bits
303-
o2::utils::EnumFlags<TestEnumLong> test;
304-
test.set("Bit32");
305-
BOOST_CHECK(test.test(TestEnumLong::Bit32));
320+
// Also tests space delimiter and construction from string.
321+
o2::utils::EnumFlags<TestEnumLong> test("Bit32 Bit34");
322+
BOOST_CHECK(test.test(TestEnumLong::Bit32, TestEnumLong::Bit34));
323+
BOOST_CHECK(!test.test(TestEnumLong::Bit1, TestEnumLong::Bit23));
306324
}
307325
}

0 commit comments

Comments
 (0)