@@ -104,8 +104,8 @@ struct FlagsHelper final {
104104
105105 // Range that is scanned by the compiler
106106 static constexpr size_t MinScan{0 };
107- static constexpr size_t MarginScan{1 }; // Scan one past to check for overpopulation
108- static constexpr size_t MaxUnderScan{std::numeric_limits<U >::digits}; // Maximum digits the underlying type has
107+ static constexpr size_t MarginScan{1 }; // Scan one past to check for overpopulation
108+ static constexpr size_t MaxUnderScan{std::numeric_limits<UMax >::digits}; // Maximum digits the underlying type has
109109 static constexpr size_t MaxScan{MaxUnderScan + MarginScan};
110110
111111 // Checks if a given 'localation' contains an enum.
@@ -161,7 +161,7 @@ struct FlagsHelper final {
161161 static constexpr auto Max_v{Values.back ()}; // Enum last entry
162162 static constexpr auto Min_u_v{static_cast <size_t >(Min_v)}; // Enum first entry as size_t
163163 static constexpr auto Max_u_v{static_cast <size_t >(Max_v)}; // Enum last entry as size_t
164- static_assert (Max_u_v < std::numeric_limits<U>::digits, " Max Bit is beyond allow range defered from underlying type" );
164+ static_assert (Max_u_v < std::numeric_limits<U>::digits, " Max Bit is beyond allow range deferred from underlying type" );
165165 static constexpr bool isContinuous () noexcept { return (Max_u_v - Min_u_v + 1 ) == count (); } // Is the enum continuous
166166 static constexpr UMax makeMaxRep (size_t min, size_t max)
167167 {
@@ -258,7 +258,7 @@ struct FlagsHelper final {
258258 static constexpr std::optional<E> fromString (std::string_view str) noexcept
259259 {
260260 for (size_t i{0 }; i < count (); ++i) {
261- if (Names[i] == str || NamesScoped[i] == str) {
261+ if (isIEqual ( Names[i], str) || isIEqual ( NamesScoped[i], str) ) {
262262 return Values[i];
263263 }
264264 }
@@ -277,7 +277,7 @@ struct FlagsHelper final {
277277 return toLower (a) == toLower (b);
278278 }
279279
280- // Case-insensitive comparision for string_view.
280+ // Case-insensitive comparison for string_view.
281281 static constexpr bool isIEqual (std::string_view s1, std::string_view s2) noexcept
282282 {
283283 if (s1.size () != s2.size ()) {
@@ -294,7 +294,7 @@ struct FlagsHelper final {
294294 static constexpr std::string_view None{" none" };
295295 static constexpr bool hasNone () noexcept
296296 {
297- // check that enum does not contain memeber named 'none'
297+ // check that enum does not contain member named 'none'
298298 for (size_t i{0 }; i < count (); ++i) {
299299 if (isIEqual (Names[i], None)) {
300300 return true ;
@@ -306,7 +306,7 @@ struct FlagsHelper final {
306306 static constexpr std::string_view All{" all" };
307307 static constexpr bool hasAll () noexcept
308308 {
309- // check that enum does not contain memeber named 'all'
309+ // check that enum does not contain member named 'all'
310310 for (size_t i{0 }; i < count (); ++i) {
311311 if (isIEqual (Names[i], All)) {
312312 return true ;
@@ -332,7 +332,7 @@ concept EnumFlag = requires {
332332};
333333
334334/* *
335- * \brief Classs to aggregate and manage enum-based on-off flags.
335+ * \brief Class to aggregate and manage enum-based on-off flags.
336336 *
337337 * This class manages flags as bits in the underlying type of an enum (upto 64 bits), allowing
338338 * manipulation via enum member names. It supports operations akin to std::bitset
@@ -355,6 +355,7 @@ concept EnumFlag = requires {
355355template <EnumFlag E>
356356class EnumFlags
357357{
358+ static constexpr int DefaultBase{2 };
358359 using H = details::enum_flags::FlagsHelper<E>;
359360 using U = std::underlying_type_t <E>;
360361 U mBits {0 };
@@ -388,9 +389,9 @@ class EnumFlags
388389 std::for_each (flags.begin (), flags.end (), [this ](const E f) noexcept { mBits |= to_bit (f); });
389390 }
390391 // Init from a string.
391- EnumFlags (const std::string& str)
392+ EnumFlags (std::string_view str, int base = DefaultBase )
392393 {
393- set (str);
394+ set (str, base );
394395 }
395396 // Destructor.
396397 constexpr ~EnumFlags () = default ;
@@ -413,14 +414,14 @@ class EnumFlags
413414 // Sets flags from a string representation.
414415 // This can be either from a number representation (binary or digits) or
415416 // a concatenation of the enums members name e.g., 'Enum1|Enum2|...'
416- void set (const std::string& s = " " , int base = 2 )
417+ void set (std::string_view s , int base = DefaultBase )
417418 {
418- // on throw restore previous state and rethrow
419- const U prev = mBits ;
420- reset ();
421419 if (s.empty ()) { // no-op
422420 return ;
423421 }
422+ // on throw restore previous state and rethrow
423+ const U prev = mBits ;
424+ reset ();
424425 try {
425426 setImpl (s, base);
426427 } catch (const std::exception& e) {
@@ -441,39 +442,42 @@ class EnumFlags
441442 }
442443
443444 // Resets a specific flag.
444- template <typename T>
445- requires std::is_same_v<T, E>
445+ template <std::same_as<E> T>
446446 constexpr void reset (T t)
447447 {
448448 mBits &= ~to_bit (t);
449449 }
450450
451451 // Tests if a specific flag is set.
452- template <typename T>
453- requires std::is_same_v<T, E>
452+ template <std::same_as<E> T>
454453 [[nodiscard]] constexpr bool test (T t) const noexcept
455454 {
456455 return (mBits & to_bit (t)) != None;
457456 }
458457
459458 // Tests if all specified flags are set.
460- template <typename ... Ts>
459+ template <std::same_as<E> ... Ts>
461460 [[nodiscard]] constexpr bool test (Ts... flags) const noexcept
462461 {
463462 return ((test (flags) && ...));
464463 }
465464
466465 // Sets a specific flag.
467- template <typename T>
468- requires std::is_same_v<T, E>
466+ template <std::same_as<E> T>
469467 constexpr void set (T t) noexcept
470468 {
471469 mBits |= to_bit (t);
472470 }
473471
472+ // Sets multiple specific flags.
473+ template <std::same_as<E>... Ts>
474+ constexpr void set (Ts... flags) noexcept
475+ {
476+ (set (flags), ...);
477+ }
478+
474479 // Toggles a specific flag.
475- template <typename T>
476- requires std::is_same_v<T, E>
480+ template <std::same_as<E> T>
477481 constexpr void toggle (T t) noexcept
478482 {
479483 mBits ^= to_bit (t);
@@ -538,8 +542,7 @@ class EnumFlags
538542 }
539543
540544 // Check if given flag is set.
541- template <typename T>
542- requires std::is_same_v<T, E>
545+ template <std::same_as<E> T>
543546 [[nodiscard]] constexpr bool operator [](const T t) const noexcept
544547 {
545548 return test (t);
@@ -564,26 +567,23 @@ class EnumFlags
564567 constexpr EnumFlags& operator =(EnumFlags&& o) = default ;
565568
566569 // Performs a bitwise OR with a flag.
567- template <typename T>
568- requires std::is_same_v<T, E>
570+ template <std::same_as<E> T>
569571 constexpr EnumFlags& operator |=(T t) noexcept
570572 {
571573 mBits |= to_bit (t);
572574 return *this ;
573575 }
574576
575577 // Performs a bitwise AND with a flag.
576- template <typename T>
577- requires std::is_same_v<T, E>
578+ template <std::same_as<E> T>
578579 constexpr EnumFlags& operator &=(T t) noexcept
579580 {
580581 mBits &= to_bit (t);
581582 return *this ;
582583 }
583584
584585 // Returns a flag set with a bitwise AND.
585- template <typename T>
586- requires std::is_same_v<T, E>
586+ template <std::same_as<E> T>
587587 constexpr EnumFlags operator &(T t) const noexcept
588588 {
589589 return EnumFlags (mBits & to_bit (t));
@@ -683,34 +683,91 @@ class EnumFlags
683683
684684 private:
685685 // Set implementation, bits was zeroed before.
686- void setImpl (const std::string& s, int base = 2 )
686+ void setImpl (std::string_view s, int base = 2 )
687687 {
688+ // Helper to check if character is valid for given base
689+ auto isValidForBase = [](unsigned char c, int base) -> bool {
690+ if (base == 2 ) {
691+ return c == ' 0' || c == ' 1' ;
692+ }
693+ if (base == 10 ) {
694+ return std::isdigit (c);
695+ }
696+ if (base == 16 ) {
697+ return std::isdigit (c) || (c >= ' a' && c <= ' f' ) || (c >= ' A' && c <= ' F' );
698+ }
699+ return false ;
700+ };
701+
702+ // hex
703+ if (base == 16 ) {
704+ std::string_view hex_str = s;
705+ // Strip optional 0x or 0X prefix
706+ if (s.size () >= 2 && s[0 ] == ' 0' && (s[1 ] == ' x' || s[1 ] == ' X' )) {
707+ hex_str = s.substr (2 );
708+ }
709+ if (hex_str.empty ()) {
710+ throw std::invalid_argument (" Empty hexadecimal string." );
711+ }
712+ if (!std::all_of (hex_str.begin (), hex_str.end (), [&](unsigned char c) { return isValidForBase (c, 16 ); })) {
713+ throw std::invalid_argument (" Invalid hexadecimal string." );
714+ }
715+ typename H::UMax v = std::stoul (std::string (hex_str), nullptr , 16 );
716+ if (v > H::MaxRep) {
717+ throw std::out_of_range (" Value exceeds enum range." );
718+ }
719+ mBits = static_cast <U>(v);
720+ return ;
721+ }
722+
723+ // decimal and binary
688724 if (std::all_of (s.begin (), s.end (), [](unsigned char c) { return std::isdigit (c); })) {
689- if (base == 2 ) { // check of only 0 and 1 in string
690- if (!std::all_of (s.begin (), s.end (), [](char c) { return c == ' 0' || c == ' 1' ; })) {
725+ if (base == 2 ) {
726+ // Binary: check only 0 and 1
727+ if (!std::all_of (s.begin (), s.end (), [&](unsigned char c) { return isValidForBase (c, 2 ); })) {
691728 throw std::invalid_argument (" Invalid binary string." );
692729 }
693730 }
694- typename H::UMax v = std::stoul (s , nullptr , base);
731+ typename H::UMax v = std::stoul (std::string (s) , nullptr , base);
695732 if (v > H::MaxRep) {
696- throw std::out_of_range (" Values exceeds enum range." );
733+ throw std::out_of_range (" Value exceeds enum range." );
697734 }
698735 mBits = static_cast <U>(v);
699- } else if (std::all_of (s.begin (), s.end (), [](unsigned char c) { return std::isalnum (c) != 0 || c == ' |' || c == ' ' || c == ' :' || c == ' ,' || c == ' ;' ; })) {
736+ }
737+ // enum name strings
738+ else if (std::all_of (s.begin (), s.end (), [](unsigned char c) { return std::isalnum (c) != 0 || c == ' |' || c == ' ' || c == ' :' || c == ' ,' || c == ' ;' ; })) {
700739 std::string cs{s};
701740 std::transform (cs.begin (), cs.end (), cs.begin (), [](unsigned char c) { return std::tolower (c); });
741+
702742 if (cs == H::All) {
703743 mBits = All;
704744 } else if (cs == H::None) {
705745 mBits = None;
706746 } else {
707- // accept as delimiter ' ', '|', ';', ','
747+ // Detect delimiter and ensure only one type is used
708748 char token = ' ' ;
709- std::string::size_type pos = s.find_first_of (" ,|;" );
710- if (pos != std::string::npos) {
711- token = s[pos];
749+ size_t pipePos = s.find (' |' );
750+ size_t commaPos = s.find (' ,' );
751+ size_t semiPos = s.find (' ;' );
752+
753+ // Count how many different delimiters exist
754+ int delimiterCount = (pipePos != std::string_view::npos ? 1 : 0 ) +
755+ (commaPos != std::string_view::npos ? 1 : 0 ) +
756+ (semiPos != std::string_view::npos ? 1 : 0 );
757+
758+ if (delimiterCount > 1 ) {
759+ throw std::invalid_argument (" Mixed delimiters not allowed!" );
712760 }
713- for (const auto & tok : Str::tokenize (s, token)) {
761+
762+ if (pipePos != std::string_view::npos) {
763+ token = ' |' ;
764+ } else if (commaPos != std::string_view::npos) {
765+ token = ' ,' ;
766+ } else if (semiPos != std::string_view::npos) {
767+ token = ' ;' ;
768+ }
769+
770+ for (const auto & tok : Str::tokenize (std::string (s), token)) {
714771 if (auto e = H::fromString (tok)) {
715772 mBits |= to_bit (*e);
716773 } else {
0 commit comments