Skip to content

Commit 429846d

Browse files
f3schsawenzel
authored andcommitted
Common: Minor cleanup of flag helper class
Signed-off-by: Felix Schlepper <felix.schlepper@cern.ch>
1 parent aa8b6fb commit 429846d

File tree

2 files changed

+107
-74
lines changed

2 files changed

+107
-74
lines changed

Common/Utils/include/CommonUtils/EnumFlags.h

Lines changed: 65 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ concept EnumFlagHelper = requires {
5757
template <EnumFlagHelper E>
5858
struct FlagsHelper final {
5959
using U = std::underlying_type_t<E>;
60+
using UMax = uint64_t; // max represetable type
6061

6162
static constexpr bool isScoped() noexcept
6263
{
@@ -144,8 +145,8 @@ struct FlagsHelper final {
144145
{
145146
constexpr std::array<bool, sizeof...(I)> valid{isValid<static_cast<E>(MinScan + I)>()...};
146147
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!");
148+
static_assert(count > 0, "EnumFlag requires at least one enum value. Check that your enum has consecutive values starting from 0.");
149+
static_assert(count <= MaxUnderScan, "Too many enum values for underlying type. Consider using a larger underlying type or fewer enum values.");
149150
std::array<E, count> values{};
150151
for (size_t idx{}, n{}; n < count; ++idx) {
151152
if (valid[idx]) {
@@ -161,8 +162,16 @@ struct FlagsHelper final {
161162
static constexpr auto Min_u_v{static_cast<size_t>(Min_v)}; // Enum first entry as size_t
162163
static constexpr auto Max_u_v{static_cast<size_t>(Max_v)}; // Enum last entry as size_t
163164
static_assert(Max_u_v < std::numeric_limits<U>::digits, "Max Bit is beyond allow range defered from underlying type");
164-
static constexpr bool isContinuous() noexcept { return (Max_u_v - Min_u_v + 1) == count(); } // Is the enum continuous
165-
static constexpr auto MaxRep{((1ULL << (static_cast<unsigned long long>(Max_u_v - Min_u_v) + 1ULL)) - 1ULL) << Min_u_v}; // largest representable value
165+
static constexpr bool isContinuous() noexcept { return (Max_u_v - Min_u_v + 1) == count(); } // Is the enum continuous
166+
static constexpr UMax makeMaxRep(size_t min, size_t max)
167+
{
168+
const size_t width = max - min + 1;
169+
if (width >= std::numeric_limits<UMax>::digits) {
170+
return std::numeric_limits<UMax>::max();
171+
}
172+
return ((UMax(1) << width) - 1) << min;
173+
}
174+
static constexpr auto MaxRep{makeMaxRep(Min_u_v, Max_u_v)}; // largest representable value
166175

167176
template <E e>
168177
static constexpr std::string_view getName()
@@ -173,7 +182,7 @@ struct FlagsHelper final {
173182
}
174183
if constexpr (tpeek_v<e>[tp + getSpec<SVal::Start, SType::Enum_t>().size()] == getSpec<SVal::AnonStart, SType::Enum_t>()) {
175184
#if defined __clang__
176-
if constexpr (tpeek_v<e>[tp + getSpec<SVal::Start, SType::enum_t>().size() + 1] == getSpec<SVal::AnonStart, SType::Enum_t>()) {
185+
if constexpr (tpeek_v<e>[tp + getSpec<SVal::Start, SType::Enum_t>().size() + 1] == getSpec<SVal::AnonStart, SType::Enum_t>()) {
177186
return {};
178187
}
179188
#endif
@@ -215,7 +224,7 @@ struct FlagsHelper final {
215224
template <E e>
216225
static constexpr auto getNameValue{getName<e>()};
217226

218-
template <bool with_scope, std::size_t... I>
227+
template <bool with_scope, size_t... I>
219228
static constexpr auto getNames(std::index_sequence<I...> /*unused*/)
220229
{
221230
if constexpr (with_scope) {
@@ -248,7 +257,7 @@ struct FlagsHelper final {
248257

249258
static constexpr std::optional<E> fromString(std::string_view str) noexcept
250259
{
251-
for (std::size_t i{0}; i < count(); ++i) {
260+
for (size_t i{0}; i < count(); ++i) {
252261
if (Names[i] == str || NamesScoped[i] == str) {
253262
return Values[i];
254263
}
@@ -325,7 +334,7 @@ concept EnumFlag = requires {
325334
/**
326335
* \brief Classs to aggregate and manage enum-based on-off flags.
327336
*
328-
* This class manages flags as bits in the underlying type of an enum, allowing
337+
* This class manages flags as bits in the underlying type of an enum (upto 64 bits), allowing
329338
* manipulation via enum member names. It supports operations akin to std::bitset
330339
* but is fully constexpr and is ideal for aggregating multiple on-off booleans,
331340
* e.g., enabling/disabling algorithm features.
@@ -371,13 +380,18 @@ class EnumFlags
371380
constexpr EnumFlags(const EnumFlags&) = default;
372381
// Move constructor.
373382
constexpr EnumFlags(EnumFlags&&) = default;
374-
// Constructor to initialize with the underlyiny type.
383+
// Constructor to initialize with the underlying type.
375384
constexpr explicit EnumFlags(U u) : mBits(u) {}
376385
// Initialize with a list of flags.
377386
constexpr EnumFlags(std::initializer_list<E> flags) noexcept
378387
{
379388
std::for_each(flags.begin(), flags.end(), [this](const E f) noexcept { mBits |= to_bit(f); });
380389
}
390+
// Init from a string.
391+
EnumFlags(const std::string& str)
392+
{
393+
set(str);
394+
}
381395
// Destructor.
382396
constexpr ~EnumFlags() = default;
383397

@@ -415,7 +429,7 @@ class EnumFlags
415429
}
416430
}
417431
// Returns the raw bitset value.
418-
constexpr auto value() const noexcept
432+
[[nodiscard]] constexpr auto value() const noexcept
419433
{
420434
return mBits;
421435
}
@@ -442,6 +456,13 @@ class EnumFlags
442456
return (mBits & to_bit(t)) != None;
443457
}
444458

459+
// Tests if all specified flags are set.
460+
template <typename... Ts>
461+
[[nodiscard]] constexpr bool test(Ts... flags) const noexcept
462+
{
463+
return ((test(flags) && ...));
464+
}
465+
445466
// Sets a specific flag.
446467
template <typename T>
447468
requires std::is_same_v<T, E>
@@ -464,6 +485,12 @@ class EnumFlags
464485
return mBits != None;
465486
}
466487

488+
// Checks if all flags are set.
489+
[[nodiscard]] constexpr bool all() const noexcept
490+
{
491+
return mBits == All;
492+
}
493+
467494
// Returns the bitset as a binary string.
468495
[[nodiscard]] std::string string() const
469496
{
@@ -505,27 +532,27 @@ class EnumFlags
505532
}
506533

507534
// Checks if any flag is set (Boolean context).
508-
constexpr explicit operator bool() const noexcept
535+
[[nodiscard]] constexpr explicit operator bool() const noexcept
509536
{
510537
return any();
511538
}
512539

513540
// Check if given flag is set.
514541
template <typename T>
515542
requires std::is_same_v<T, E>
516-
constexpr bool operator[](const T t) noexcept
543+
[[nodiscard]] constexpr bool operator[](const T t) const noexcept
517544
{
518545
return test(t);
519546
}
520547

521548
// Checks if two flag sets are equal.
522-
constexpr bool operator==(const EnumFlags& o) const noexcept
549+
[[nodiscard]] constexpr bool operator==(const EnumFlags& o) const noexcept
523550
{
524551
return mBits == o.mBits;
525552
}
526553

527554
// Checks if two flag sets are not equal.
528-
constexpr bool operator!=(const EnumFlags& o) const noexcept
555+
[[nodiscard]] constexpr bool operator!=(const EnumFlags& o) const noexcept
529556
{
530557
return mBits != o.mBits;
531558
}
@@ -584,7 +611,13 @@ class EnumFlags
584611
// Performs a bitwise XOR with another flag set.
585612
constexpr EnumFlags operator^(const EnumFlags& o) const noexcept
586613
{
587-
return Flags(mBits ^ o.mBits);
614+
return EnumFlags(mBits ^ o.mBits);
615+
}
616+
617+
// Performs a bitwise and with another flag set.
618+
constexpr EnumFlags operator&(const EnumFlags& o) const noexcept
619+
{
620+
return EnumFlags(mBits & o.mBits);
588621
}
589622

590623
// Performs a bitwise XOR assignment.
@@ -596,14 +629,14 @@ class EnumFlags
596629

597630
// Checks if all specified flags are set.
598631
template <typename... Ts>
599-
constexpr bool all_of(Ts... flags) const noexcept
632+
[[nodiscard]] constexpr bool all_of(Ts... flags) const noexcept
600633
{
601-
return ((test(flags) && ...));
634+
return test(flags...);
602635
}
603636

604637
// Checks if none of the specified flags are set.
605638
template <typename... Ts>
606-
constexpr bool none_of(Ts... flags) const noexcept
639+
[[nodiscard]] constexpr bool none_of(Ts... flags) const noexcept
607640
{
608641
return (!(test(flags) || ...));
609642
}
@@ -617,7 +650,7 @@ class EnumFlags
617650
// Deserializes a string into the flag set.
618651
void deserialize(const std::string& data)
619652
{
620-
uint64_t v = std::stoul(data);
653+
typename H::UMax v = std::stoul(data);
621654
if (v > H::MaxRep) {
622655
throw std::out_of_range("Values exceeds enum range.");
623656
}
@@ -627,35 +660,29 @@ class EnumFlags
627660
// Counts the number of set bits (active flags).
628661
[[nodiscard]] constexpr size_t count() const noexcept
629662
{
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;
663+
return std::popcount(mBits);
637664
}
638665

639666
// Returns the union of two flag sets.
640-
constexpr EnumFlags union_with(const EnumFlags& o) const noexcept
667+
[[nodiscard]] constexpr EnumFlags union_with(const EnumFlags& o) const noexcept
641668
{
642669
return EnumFlags(mBits | o.mBits);
643670
}
644671

645672
// Returns the intersection of two flag sets.
646-
constexpr EnumFlags intersection_with(const EnumFlags& o) const noexcept
673+
[[nodiscard]] constexpr EnumFlags intersection_with(const EnumFlags& o) const noexcept
647674
{
648675
return EnumFlags(mBits & o.mBits);
649676
}
650677

651678
// Checks if all flags in another Flags object are present in the current object.
652-
constexpr bool contains(const EnumFlags& other) const noexcept
679+
[[nodiscard]] constexpr bool contains(const EnumFlags& other) const noexcept
653680
{
654681
return (mBits & other.mBits) == other.mBits;
655682
}
656683

657684
private:
658-
// Set implemnetation, bits was zeroed before.
685+
// Set implementation, bits was zeroed before.
659686
void setImpl(const std::string& s, int base = 2)
660687
{
661688
if (std::all_of(s.begin(), s.end(), [](unsigned char c) { return std::isdigit(c); })) {
@@ -664,20 +691,25 @@ class EnumFlags
664691
throw std::invalid_argument("Invalid binary string.");
665692
}
666693
}
667-
uint64_t v = std::stoul(s, nullptr, base);
694+
typename H::UMax v = std::stoul(s, nullptr, base);
668695
if (v > H::MaxRep) {
669696
throw std::out_of_range("Values exceeds enum range.");
670697
}
671698
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 == ','; })) {
699+
} else if (std::all_of(s.begin(), s.end(), [](unsigned char c) { return std::isalnum(c) != 0 || c == '|' || c == ' ' || c == ':' || c == ',' || c == ';'; })) {
673700
std::string cs{s};
674701
std::transform(cs.begin(), cs.end(), cs.begin(), [](unsigned char c) { return std::tolower(c); });
675702
if (cs == H::All) {
676703
mBits = All;
677704
} else if (cs == H::None) {
678705
mBits = None;
679706
} else {
680-
char token = (s.find(',') != std::string::npos) ? ',' : '|';
707+
// accept as delimiter ' ', '|', ';', ','
708+
char token = ' ';
709+
std::string::size_type pos = s.find_first_of(",|;");
710+
if (pos != std::string::npos) {
711+
token = s[pos];
712+
}
681713
for (const auto& tok : Str::tokenize(s, token)) {
682714
if (auto e = H::fromString(tok)) {
683715
mBits |= to_bit(*e);

0 commit comments

Comments
 (0)