11#ifndef O2_FRAMEWORK_FLAGS_H_
12#define O2_FRAMEWORK_FLAGS_H_
19#include <source_location>
29#include <initializer_list>
45namespace details::enum_flags
51 requires std::is_enum_v<E>;
52 requires std::is_unsigned_v<std::underlying_type_t<E>>;
53 requires std::same_as<E, std::decay_t<E>>;
62template <EnumFlagHelper E>
64 using U = std::underlying_type_t<E>;
66 static_assert(std::numeric_limits<U>::digits <= std::numeric_limits<UMax>::digits,
"Underlying type has more digits than max supported digits");
70 return std::is_enum_v<E> && !std::is_convertible_v<E, std::underlying_type_t<E>>;
75 static consteval const char*
tpeek() noexcept
77 return std::source_location::current().function_name();
81 static constexpr std::string_view
tpeek_v{tpeek<e>()};
85 std::tuple<std::string_view, char, std::string_view, char>>({
87 {
"e = ",
']',
"(anonymous namespace)",
'('},
88 {
"T = ",
']',
"(anonymous namespace)",
'('},
90 {
"e = ",
';',
"<unnamed>",
'<'},
91 {
"T = ",
']',
"{anonymous}",
'{'},
105 template <SVal v, SType t>
108 return std::get<static_cast<size_t>(
v)>(
CSpecifics[
static_cast<size_t>(t)]);
121 constexpr auto tp{tpeek_v<e>.rfind(getSpec<SVal::Start, SType::Enum_t>())};
122 if constexpr (tp == std::string_view::npos) {
126 else if constexpr (tpeek_v<e>[tp + getSpec<SVal::Start, SType::Enum_t>().size()] ==
'(') {
127 if constexpr (tpeek_v<e>[tp + getSpec<SVal::Start, SType::Enum_t>().size() + 1] ==
'(') {
130 if constexpr (tpeek_v<e>.find(getSpec<SVal::AnonStr, SType::Enum_t>(), tp + getSpec<SVal::Start, SType::Enum_t>().size()) != std::string_view::npos) {
133 }
else if constexpr (tpeek_v<e>.find_first_of(getSpec<SVal::End, SType::Enum_t>(), tp + getSpec<SVal::Start, SType::Enum_t>().size()) != std::string_view::npos) {
138 else if constexpr (tpeek_v<e>[tp + getSpec<SVal::Start, SType::Enum_t>().size()] !=
'(' && tpeek_v<e>.find_first_of(getSpec<SVal::End, SType::Enum_t>(), tp + getSpec<SVal::Start, SType::Enum_t>().
size()) != std::string_view::npos) {
142#error Unsupported compiler
149 template <
size_t... I>
150 static constexpr auto getValues(std::index_sequence<I...> )
noexcept
152 constexpr std::array<bool,
sizeof...(I)>
valid{isValid<static_cast<E>(
MinScan + I)>()...};
153 constexpr auto count{std::count_if(
valid.cbegin(),
valid.cend(), [](
bool v)
noexcept { return v; })};
154 static_assert(
count > 0,
"EnumFlag requires at least one enum value. Check that your enum has consecutive values starting from 0.");
155 static_assert(
count <=
MaxUnderScan,
"Too many enum values for underlying type. Consider using a larger underlying type or fewer enum values.");
156 std::array<E, count>
values{};
157 for (
size_t idx{},
n{};
n <
count; ++idx) {
164 static constexpr auto Values{
getValues(std::make_index_sequence<MaxScan - MinScan - MarginScan>())};
170 static_assert(Max_u_v < std::numeric_limits<U>::digits,
"Max Bit is beyond allow range deferred from underlying type");
175 if (
width >= std::numeric_limits<UMax>::digits) {
176 return std::numeric_limits<UMax>::max();
185 constexpr auto tp{tpeek_v<e>.rfind(getSpec<SVal::Start, SType::Enum_t>())};
186 if constexpr (tp == std::string_view::npos) {
189 if constexpr (tpeek_v<e>[tp + getSpec<SVal::Start, SType::Enum_t>().size()] == getSpec<SVal::AnonStart, SType::Enum_t>()) {
191 if constexpr (tpeek_v<e>[tp + getSpec<SVal::Start, SType::Enum_t>().size() + 1] == getSpec<SVal::AnonStart, SType::Enum_t>()) {
195 if (
constexpr auto lstr{tpeek_v<e>.substr(tp + getSpec<SVal::Start, SType::Enum_t>().
size())}; lstr.find(getSpec<SVal::AnonStr, SType::Enum_t>()) != std::string_view::npos) {
196 if constexpr (
constexpr auto lc{lstr.find_first_of(getSpec<SVal::End, SType::Enum_t>())}; lc != std::string_view::npos) {
197 return lstr.substr(getSpec<SVal::AnonStr, SType::Enum_t>().size() + 2, lc - (getSpec<SVal::AnonStr, SType::Enum_t>().size() + 2));
201 constexpr std::string_view
result{tpeek_v<e>.substr(tp + getSpec<SVal::Start, SType::Enum_t>().size())};
202 if constexpr (
constexpr auto lc{
result.find_first_of(getSpec<SVal::End, SType::Enum_t>())}; lc != std::string_view::npos) {
203 return result.substr(0, lc);
211 if (
const auto lc{s.find_last_of(
':')}; lc != std::string_view::npos) {
212 return s.substr(lc + 1);
217 static constexpr std::string_view
findScope(std::string_view s)
219 const auto pos1 = s.rfind(
"::");
220 if (pos1 == std::string_view::npos) {
223 const auto pos2 = s.rfind(
"::", pos1 - 1);
224 if (pos2 == std::string_view::npos) {
225 return s.substr(0, pos1);
227 return s.substr(pos2 + 2, pos1 - pos2 - 2);
233 template <
bool with_scope,
size_t... I>
234 static constexpr auto getNames(std::index_sequence<I...> )
236 if constexpr (with_scope) {
237 return std::array<std::string_view,
sizeof...(I)>{getNameValue<Values[I]>...};
243 static constexpr auto Names{getNames<false>(std::make_index_sequence<
count()>())};
250 for (
size_t i{0};
i <
count(); ++
i) {
259 static constexpr std::string_view
toString() noexcept
261 return getNameValue<e>();
266 for (
size_t i{0};
i <
count(); ++
i) {
275 static constexpr unsigned char toLower(
const unsigned char c)
noexcept
277 return (
c >=
'A' &&
c <=
'Z') ? (
c -
'A' +
'a') :
c;
281 static constexpr bool isIEqual(
const unsigned char a,
const unsigned char b)
noexcept
287 static constexpr bool isIEqual(std::string_view
s1, std::string_view s2)
noexcept
289 if (
s1.size() != s2.size()) {
292 for (
size_t i{0};
i <
s1.size(); ++
i) {
300 static constexpr std::string_view
None{
"none"};
304 for (
size_t i{0};
i <
count(); ++
i) {
312 static constexpr std::string_view
All{
"all"};
316 for (
size_t i{0};
i <
count(); ++
i) {
370 static constexpr int DefaultBase{2};
374 using U = std::underlying_type_t<E>;
378 constexpr auto to_underlying(E e)
const noexcept
380 return static_cast<U
>(e);
384 constexpr auto to_bit(E e)
const noexcept
386 return U(1) << to_underlying(e);
435 void set(
const std::string& s,
int base = DefaultBase)
441 const U prev = mBits;
445 }
catch (
const std::exception& e) {
452 [[nodiscard]]
constexpr auto value() const noexcept
464 template <std::same_as<E> T>
471 template <std::same_as<E> T>
472 [[nodiscard]]
constexpr bool test(T t)
const noexcept
474 return (mBits & to_bit(t)) !=
None;
478 template <std::same_as<E>... Ts>
485 template <std::same_as<E> T>
486 constexpr void set(T t)
noexcept
492 template <std::same_as<E>... Ts>
499 template <std::same_as<E> T>
506 [[nodiscard]]
constexpr bool any() const noexcept
508 return mBits !=
None;
513 [[nodiscard]]
constexpr bool all() const noexcept
521 std::ostringstream oss;
522 oss << std::bitset<H::count()>(mBits);
527 [[nodiscard]] std::string
pstring(
bool withNewline =
false)
const
529 std::ostringstream oss;
546 for (
size_t a{2 +
i}; --
a != 0U;) {
560 [[nodiscard]]
constexpr explicit operator bool() const noexcept
566 template <std::same_as<E> T>
567 [[nodiscard]]
constexpr bool operator[](
const T t)
const noexcept
575 return mBits == o.mBits;
581 return mBits != o.mBits;
591 template <std::same_as<E> T>
599 template <std::same_as<E> T>
607 template <std::same_as<E> T>
652 template <
typename...
Ts>
659 template <
typename...
Ts>
677 throw std::out_of_range(
"Values exceeds enum range.");
679 mBits =
static_cast<U
>(
v);
684 [[nodiscard]]
constexpr size_t count() const noexcept
686 return std::popcount(mBits);
710 void setImpl(
const std::string& s,
int base = 2)
713 auto isValidForBase = [](
unsigned char c,
int base) ->
bool {
715 return c ==
'0' ||
c ==
'1';
718 return std::isdigit(
c);
721 return std::isdigit(
c) || (
c >=
'a' &&
c <=
'f') || (
c >=
'A' &&
c <=
'F');
728 std::string_view hex_str{
s};
730 if (
s.size() >= 2 &&
s[0] ==
'0' && (
s[1] ==
'x' ||
s[1] ==
'X')) {
731 hex_str.remove_prefix(2);
733 if (hex_str.empty()) {
734 throw std::invalid_argument(
"Empty hexadecimal string.");
736 if (!std::all_of(hex_str.begin(), hex_str.end(), [&](
unsigned char c) { return isValidForBase(c, 16); })) {
737 throw std::invalid_argument(
"Invalid hexadecimal string.");
739 typename H::UMax v = std::stoul(std::string(hex_str),
nullptr, 16);
741 throw std::out_of_range(
"Value exceeds enum range.");
743 mBits =
static_cast<U
>(
v);
748 if (std::all_of(
s.begin(),
s.end(), [](
unsigned char c) { return std::isdigit(c); })) {
751 if (!std::all_of(
s.begin(),
s.end(), [&](
unsigned char c) { return isValidForBase(c, 2); })) {
752 throw std::invalid_argument(
"Invalid binary string.");
755 typename H::UMax v = std::stoul(std::string(s),
nullptr, base);
757 throw std::out_of_range(
"Value exceeds enum range.");
759 mBits =
static_cast<U
>(
v);
762 else if (std::all_of(
s.begin(),
s.end(), [](
unsigned char c) { return std::isalnum(c) != 0 || c ==
'|' || c ==
' ' || c ==
':' || c ==
',' || c ==
';'; })) {
764 std::transform(cs.begin(), cs.end(), cs.begin(), [](
unsigned char c) { return std::tolower(c); });
773 size_t pipePos =
s.find(
'|');
774 size_t commaPos =
s.find(
',');
775 size_t semiPos =
s.find(
';');
778 int delimiterCount = (pipePos != std::string_view::npos ? 1 : 0) +
779 (commaPos != std::string_view::npos ? 1 : 0) +
780 (semiPos !=
std::string_view::npos ? 1 : 0);
782 if (delimiterCount > 1) {
783 throw std::invalid_argument(
"Mixed delimiters not allowed!");
786 if (pipePos != std::string_view::npos) {
788 }
else if (commaPos != std::string_view::npos) {
790 }
else if (semiPos != std::string_view::npos) {
798 throw std::invalid_argument(tok +
" is not a valid enum value!");
803 throw std::invalid_argument(
"Cannot parse string!");
813 os <<
f.pstring(
true);
Class to aggregate and manage enum-based on-off flags.
constexpr bool all_of(Ts... flags) const noexcept
constexpr EnumFlags operator|(const EnumFlags &o) const noexcept
constexpr EnumFlags & operator=(const EnumFlags &o)=default
constexpr bool operator==(const EnumFlags &o) const noexcept
std::string serialize() const
constexpr size_t count() const noexcept
constexpr EnumFlags(std::initializer_list< E > flags) noexcept
constexpr bool operator!=(const EnumFlags &o) const noexcept
constexpr EnumFlags()=default
constexpr bool any() const noexcept
constexpr EnumFlags union_with(const EnumFlags &o) const noexcept
static constexpr auto getValues() noexcept
constexpr auto value() const noexcept
constexpr void reset() noexcept
constexpr EnumFlags(const EnumFlags &)=default
constexpr EnumFlags operator~() const noexcept
constexpr EnumFlags operator^(const EnumFlags &o) const noexcept
constexpr EnumFlags & operator=(EnumFlags &&o)=default
constexpr EnumFlags intersection_with(const EnumFlags &o) const noexcept
constexpr EnumFlags & operator|=(T t) noexcept
constexpr void reset(T t)
void deserialize(const std::string &data)
void set(const std::string &s, int base=DefaultBase)
constexpr bool all() const noexcept
constexpr EnumFlags(EnumFlags &&)=default
constexpr void set(Ts... flags) noexcept
constexpr bool test(T t) const noexcept
constexpr bool none_of(Ts... flags) const noexcept
constexpr EnumFlags operator&(const EnumFlags &o) const noexcept
constexpr EnumFlags operator&(T t) const noexcept
std::string string() const
static constexpr auto getNames() noexcept
constexpr EnumFlags & operator^=(const EnumFlags &o) noexcept
constexpr void set(T t) noexcept
constexpr EnumFlags & operator&=(T t) noexcept
std::string pstring(bool withNewline=false) const
constexpr bool contains(const EnumFlags &other) const noexcept
constexpr bool test(Ts... flags) const noexcept
constexpr void toggle(T t) noexcept
constexpr EnumFlags & operator|=(const EnumFlags &o) noexcept
constexpr bool operator[](const T t) const noexcept
EnumFlags(const std::string &str, int base=DefaultBase)
GLsizei const GLchar *const * string
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat s1
GLboolean GLboolean GLboolean b
GLenum GLsizei GLsizei GLint * values
GLenum GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const void * bits
GLboolean GLboolean GLboolean GLboolean a
const std::vector< std::string > tokenize(const std::string_view input, const std::string_view pattern)
std::ostream & operator<<(std::ostream &os, const EnumFlags< E > &f)
std::string to_string(gsl::span< T, Size > span)
FIXME: do not use data model tables.
Marks an empty item in the context.
static constexpr std::string_view findScope(std::string_view s)
static constexpr auto Min_u_v
static constexpr bool hasNone() noexcept
static constexpr std::string_view None
static constexpr UMax makeMaxRep(size_t min, size_t max)
static constexpr auto NamesScoped
static constexpr auto Max_u_v
static constexpr auto CSpecifics
static constexpr auto getLongestName() noexcept
static constexpr auto Min_v
static constexpr bool isIEqual(const unsigned char a, const unsigned char b) noexcept
static constexpr auto count() noexcept
std::underlying_type_t< E > U
static constexpr size_t MarginScan
static constexpr auto NamesLongest
static constexpr auto MaxRep
static constexpr auto Names
static constexpr std::string_view All
static constexpr bool isIEqual(std::string_view s1, std::string_view s2) noexcept
static constexpr size_t MinScan
static consteval const char * tpeek() noexcept
static constexpr auto getNames(std::index_sequence< I... >)
static constexpr std::string_view tpeek_v
static constexpr bool hasAll() noexcept
static constexpr std::string_view getName()
static constexpr bool isValid() noexcept
static constexpr size_t MaxScan
static constexpr auto Max_v
static constexpr unsigned char toLower(const unsigned char c) noexcept
static constexpr auto getValues(std::index_sequence< I... >) noexcept
static constexpr auto Values
static constexpr auto Scope
static constexpr std::string_view removeScope(std::string_view s)
static constexpr std::optional< E > fromString(std::string_view str) noexcept
static constexpr bool isScoped() noexcept
static constexpr bool isContinuous() noexcept
static constexpr size_t MaxUnderScan
static constexpr std::string_view toString() noexcept
static constexpr auto getNameValue
static constexpr auto getSpec() noexcept
VectorOfTObjectPtrs other