From d27703e7e9fbbd7cf6bd9d86cd0bbe84a9c67660 Mon Sep 17 00:00:00 2001 From: ZXShady <153229951+ZXShady@users.noreply.github.com> Date: Thu, 22 May 2025 18:42:09 +0200 Subject: [PATCH 1/8] Use `enchantum.hpp` --- include/rfl/internal/enums/Names.hpp | 45 +- .../rfl/internal/enums/StringConverter.hpp | 34 +- include/rfl/internal/enums/get_enum_names.hpp | 150 +- include/rfl/internal/enums/is_flag_enum.hpp | 13 +- include/rfl/internal/enums/is_scoped_enum.hpp | 4 +- include/rfl/thirdparty/enchantum.hpp | 1495 +++++++++++++++++ 6 files changed, 1587 insertions(+), 154 deletions(-) create mode 100644 include/rfl/thirdparty/enchantum.hpp diff --git a/include/rfl/internal/enums/Names.hpp b/include/rfl/internal/enums/Names.hpp index 332c2ed8..014fd03d 100644 --- a/include/rfl/internal/enums/Names.hpp +++ b/include/rfl/internal/enums/Names.hpp @@ -1,6 +1,32 @@ #ifndef RFL_INTERNAL_ENUMS_NAMES_HPP_ #define RFL_INTERNAL_ENUMS_NAMES_HPP_ +// Enum values must be greater than or equal to RFL_ENUM_RANGE_MIN. +// By default, RFL_ENUM_RANGE_MIN is set to 0. +// To change the default minimum range for all enum types, redefine the macro +// RFL_ENUM_RANGE_MIN. +#if !defined(RFL_ENUM_RANGE_MIN) +#define RFL_ENUM_RANGE_MIN -256 +#endif + +// Enum values must be less than or equal to RFL_ENUM_RANGE_MAX. +// By default, RFL_ENUM_RANGE_MAX is set to 127. +// To change the default maximum range for all enum types, redefine the macro +// RFL_ENUM_RANGE_MAX. +#if !defined(RFL_ENUM_RANGE_MAX) +#define RFL_ENUM_RANGE_MAX 256 +#endif + +#ifdef ENCHANTUM_MIN_RANGE +#undef ENCHANTUM_MIN_RANGE +#endif +#define ENCHANTUM_MIN_RANGE RFL_ENUM_RANGE_MIN + +#ifdef ENCHANTUM_MAX_RANGE +#undef ENCHANTUM_MAX_RANGE +#endif +#define ENCHANTUM_MAX_RANGE RFL_ENUM_RANGE_MAX + #include #include #include @@ -15,21 +41,6 @@ namespace rfl { -// Enum values must be greater than or equal to RFL_ENUM_RANGE_MIN. -// By default, RFL_ENUM_RANGE_MIN is set to 0. -// To change the default minimum range for all enum types, redefine the macro -// RFL_ENUM_RANGE_MIN. -#if !defined(RFL_ENUM_RANGE_MIN) -#define RFL_ENUM_RANGE_MIN 0 -#endif - -// Enum values must be less than or equal to RFL_ENUM_RANGE_MAX. -// By default, RFL_ENUM_RANGE_MAX is set to 127. -// To change the default maximum range for all enum types, redefine the macro -// RFL_ENUM_RANGE_MAX. -#if !defined(RFL_ENUM_RANGE_MAX) -#define RFL_ENUM_RANGE_MAX 127 -#endif namespace config { @@ -73,7 +84,7 @@ template enums_ = + constexpr static auto enums_ = std::array{_enums...}; static_assert(N == 0 || LiteralType::size() == N, @@ -137,3 +148,5 @@ names_to_underlying_enumerator_array( } // namespace rfl #endif + + diff --git a/include/rfl/internal/enums/StringConverter.hpp b/include/rfl/internal/enums/StringConverter.hpp index b2f3d1b6..19149002 100644 --- a/include/rfl/internal/enums/StringConverter.hpp +++ b/include/rfl/internal/enums/StringConverter.hpp @@ -10,6 +10,7 @@ #include "../../Result.hpp" #include "../../internal/strings/strings.hpp" #include "../../type_name_t.hpp" +#include "../../thirdparty/enchantum.hpp" #include "get_enum_names.hpp" #include "is_flag_enum.hpp" @@ -69,18 +70,10 @@ class StringConverter { /// This assumes that _enum can be exactly matched to one of the names and /// does not have to be combined using |. static std::string enum_to_single_string(const EnumType _enum) { - const auto to_str = [](const auto _l) { return _l.str(); }; - - for (size_t i = 0; i < names_.size; ++i) { - if (names_.enums_[i] == _enum) { - return NamesLiteral::from_value( - static_cast(i)) - .transform(to_str) - .value(); - } - } - - return std::to_string(static_cast>(_enum)); + const auto s = enchantum::to_string(_enum); + return s.empty() ? std::to_string( + static_cast>(_enum)) + : std::string(s); } /// Finds the enum matching the literal. @@ -91,15 +84,14 @@ class StringConverter { /// This assumes that _enum can be exactly matched to one of the names and /// does not have to be combined using |. static Result single_string_to_enum(const std::string& _str) { - const auto r = NamesLiteral::from_string(_str).transform(literal_to_enum); - if (r) { - return r; - } else { - try { - return static_cast(std::stoi(_str)); - } catch (std::exception& exp) { - return error(exp.what()); - } + const auto r = enchantum::cast(_str); + if (r) + return *r; + + try { + return static_cast(std::stoi(_str)); + } catch (std::exception& exp) { + return error(exp.what()); } } diff --git a/include/rfl/internal/enums/get_enum_names.hpp b/include/rfl/internal/enums/get_enum_names.hpp index d5a31569..6fe49ad5 100644 --- a/include/rfl/internal/enums/get_enum_names.hpp +++ b/include/rfl/internal/enums/get_enum_names.hpp @@ -10,10 +10,8 @@ #endif #include "../../Literal.hpp" -#include "../../define_literal.hpp" -#include "../../internal/remove_namespaces.hpp" +#include "../../thirdparty/enchantum.hpp" #include "Names.hpp" -#include "is_scoped_enum.hpp" // https://en.cppreference.com/w/cpp/language/static_cast: @@ -41,43 +39,6 @@ namespace rfl::internal::enums { -template -consteval auto get_enum_name_str_view() { -#if __cpp_lib_source_location >= 201907L - const auto func_name = - std::string_view{std::source_location::current().function_name()}; -#elif defined(_MSC_VER) - // Officially, we only support MSVC versions that are modern enough to contain - // , but inofficially, this might work. - const auto func_name = std::string_view{__FUNCSIG__}; -#else - const auto func_name = std::string_view{__PRETTY_FUNCTION__}; -#endif -#if defined(__clang__) - const auto split = func_name.substr(0, func_name.size() - 1); - return split.substr(split.find("e = ") + 4); -#elif defined(__GNUC__) - const auto split = func_name.substr(0, func_name.size() - 1); - return split.substr(split.find("e = ") + 4); -#elif defined(_MSC_VER) - const auto split = func_name.substr(0, func_name.size() - 7); - return split.substr(split.find("get_enum_name_str_view<") + 23); -#else - static_assert(false, - "You are using an unsupported compiler. Please use GCC, Clang " - "or MSVC or use rfl::Literal."); -#endif -} - -template -consteval auto get_enum_name() { - constexpr auto name = get_enum_name_str_view(); - const auto to_str_lit = [&](std::index_sequence) { - return StringLiteral{name[Ns]...}; - }; - return to_str_lit(std::make_index_sequence{}); -} - template consteval auto get_range_min() { using U = std::underlying_type_t; @@ -105,81 +66,48 @@ consteval auto get_range_max() { } } -template -consteval T calc_j() { - if constexpr (_is_flag) { - return static_cast(1) << _i; - } else { - return static_cast(_i); - } -} - -template -consteval auto operator|(Names, - std::integral_constant) { - using NamesType = Names; - using T = std::underlying_type_t; - constexpr T j = calc_j(); - constexpr auto name = get_enum_name(j)>(); - if constexpr (std::get<0>(name.arr_) == '(') { - return NamesType{}; - } else { - return typename NamesType::template AddOneType< - Literal()>, static_cast(j)>{}; - } -} - -template -struct NamesCombiner { - template - static consteval auto combine(std::integer_sequence) { - return (Names{} | ... | - std::integral_constant()); - } -}; - -template -struct CombineNames { - template - static consteval auto apply() { - if constexpr (End - Start < ChunkSize) { - return [](std::integer_sequence) { - return (NamesType{} | ... | std::integral_constant()); - }(std::make_integer_sequence{}); - } else { - constexpr int Mid = Start + (End - Start) / 2; - constexpr auto left = - CombineNames::template apply(); - constexpr auto right = - CombineNames::template apply(); - return left | right; - } - } +struct str_view_agg { + const char* data; + std::size_t size; }; -template +template consteval auto get_enum_names() { - static_assert(is_scoped_enum, - "You must use scoped enums (using class or struct) for the " - "parsing to work!"); - static_assert(std::is_integral_v>, - "The underlying type of any Enum must be integral!"); - - constexpr auto max = get_range_max(); - constexpr auto min = get_range_min(); - constexpr auto range_size = max - min + 1; - - static_assert(range_size > 0, - "enum_range requires a valid range size. Ensure that max is " - "greater than min."); - - using EmptyNames = Names, 0, _is_flag>; - - return CombineNames<>::template apply(); + // constexpr auto max = get_range_max(); + // constexpr auto min = get_range_min(); + // constexpr auto range_size = max - min + 1; + // + // static_assert(range_size > 0, + // "enum_range requires a valid range size. Ensure that max is " + // "greater than min."); + // + // using EmptyNames = Names, 0, _is_flag>; + + constexpr auto convert = []() { + constexpr auto a = enchantum::entries; + std::array, a.size()> b; + for (std::size_t i = 0; i < a.size(); ++i) { + b[i].first = a[i].first; + b[i].second = {a[i].second.data(), a[i].second.size()}; + } + return b; + }(); + + // return Names, sizeof...(entries), _is_flag, + // entries.first...>{}; + + return [convert](std::index_sequence) { + constexpr auto to_str_lit = + [](const char* name, std::index_sequence) { + return StringLiteral{name[Js]...}; + }; + return Names{})...>, + convert.size(), _is_flag, convert[Is].first...>{}; + }(std::make_index_sequence>{}); } - } // namespace rfl::internal::enums #endif diff --git a/include/rfl/internal/enums/is_flag_enum.hpp b/include/rfl/internal/enums/is_flag_enum.hpp index 89369039..660a0fe0 100644 --- a/include/rfl/internal/enums/is_flag_enum.hpp +++ b/include/rfl/internal/enums/is_flag_enum.hpp @@ -4,16 +4,21 @@ #include #include "is_scoped_enum.hpp" +#include "../../thirdparty/enchantum.hpp" +template +requires requires(E e) { + { e | e } -> std::same_as; +} +constexpr inline bool enchantum::is_bitflag = true; namespace rfl { namespace internal { namespace enums { template -concept is_flag_enum = is_scoped_enum && - requires(EnumType e1, EnumType e2) { - { e1 | e2 } -> std::same_as; -}; +concept is_flag_enum = + is_scoped_enum && enchantum::BitFlagEnum; + } // namespace enums } // namespace internal diff --git a/include/rfl/internal/enums/is_scoped_enum.hpp b/include/rfl/internal/enums/is_scoped_enum.hpp index 958aca8a..6880d4c7 100644 --- a/include/rfl/internal/enums/is_scoped_enum.hpp +++ b/include/rfl/internal/enums/is_scoped_enum.hpp @@ -3,14 +3,14 @@ #include #include +#include "../../thirdparty/enchantum.hpp" namespace rfl { namespace internal { namespace enums { template -concept is_scoped_enum = std::is_enum_v && - !std::is_convertible_v>; +concept is_scoped_enum = enchantum::ScopedEnum; } // namespace enums } // namespace internal diff --git a/include/rfl/thirdparty/enchantum.hpp b/include/rfl/thirdparty/enchantum.hpp new file mode 100644 index 00000000..0c445349 --- /dev/null +++ b/include/rfl/thirdparty/enchantum.hpp @@ -0,0 +1,1495 @@ +#pragma once + +#ifndef ENCHANTUM_ALIAS_OPTIONAL + #include +#endif + +namespace enchantum { +#ifdef ENCHANTUM_ALIAS_OPTIONAL +ENCHANTUM_ALIAS_OPTIONAL; +#else +using ::std::optional; +#endif + +} // namespace enchantum + +#ifndef ENCHANTUM_ALIAS_STRING_VIEW + #include +#endif + +namespace enchantum { +#ifdef ENCHANTUM_ALIAS_STRING_VIEW +ENCHANTUM_ALIAS_STRING_VIEW; +#else +using ::std::string_view; +#endif + +} // namespace enchantum + +#include +#include +#include +#include + +#ifndef ENCHANTUM_ASSERT + #include +// clang-format off + #define ENCHANTUM_ASSERT(cond, msg, ...) assert(cond && msg) +// clang-format on +#endif + +#ifndef ENCHANTUM_THROW + // additional info such as local variables are here + #define ENCHANTUM_THROW(exception, ...) throw exception +#endif + +#ifndef ENCHANTUM_MAX_RANGE + #define ENCHANTUM_MAX_RANGE 256 +#endif +#ifndef ENCHANTUM_MIN_RANGE + #define ENCHANTUM_MIN_RANGE (-ENCHANTUM_MAX_RANGE) +#endif + +namespace enchantum { + +template +concept Enum = std::is_enum_v; + +template +concept SignedEnum = Enum && std::signed_integral>; + +template +concept UnsignedEnum = Enum && !SignedEnum; + +template +concept ScopedEnum = Enum && (!std::is_convertible_v>); + +template +concept UnscopedEnum = Enum && !ScopedEnum; + +template +concept EnumOfUnderlying = Enum && std::same_as, Underlying>; + +template +inline constexpr bool is_bitflag = requires(E e) { + requires std::same_as || std::same_as; + { ~e } -> std::same_as; + { e | e } -> std::same_as; + { e &= e } -> std::same_as; + { e |= e } -> std::same_as; +}; + +template +concept BitFlagEnum = Enum && is_bitflag; + +template +concept EnumFixedUnderlying = Enum && requires { T{0}; }; + +template +struct enum_traits; + +template +struct enum_traits { +private: + using U = std::underlying_type_t; + using L = std::numeric_limits; +public: + static constexpr std::size_t prefix_length = 0; + + static constexpr auto min = (L::min)() > ENCHANTUM_MIN_RANGE ? (L::min)() : ENCHANTUM_MIN_RANGE; + static constexpr auto max = (L::max)() < ENCHANTUM_MAX_RANGE ? (L::max)() : ENCHANTUM_MAX_RANGE; +}; + +template +struct enum_traits { +private: + using T = std::underlying_type_t; + using L = std::numeric_limits; +public: + static constexpr std::size_t prefix_length = 0; + + static constexpr auto min = []() { + if constexpr (std::is_same_v) + return false; + else + return (ENCHANTUM_MIN_RANGE) < 0 ? 0 : (ENCHANTUM_MIN_RANGE); + }(); + static constexpr auto max = []() { + if constexpr (std::is_same_v) + return true; + else + return (L::max)() < (ENCHANTUM_MAX_RANGE) ? (L::max)() : (ENCHANTUM_MAX_RANGE); + }(); +}; + +} // namespace enchantum +#include +#include +#include +#include + +namespace enchantum::details { + +template +inline constexpr std::size_t prefix_length_or_zero = 0; + +template +inline constexpr auto prefix_length_or_zero::prefix_length)> = std::size_t{ + enum_traits::prefix_length}; + +template +constexpr auto generate_arrays() +{ +#if defined __clang__ && __clang_major__ >= 20 + if constexpr (BitFlagEnum) { + if constexpr (EnumFixedUnderlying) { + constexpr std::size_t bits = sizeof(Enum) * CHAR_BIT; + std::array ret{}; // 0 value reflected + for (std::size_t i = 0; i < bits; ++i) + ret[i + 1] = static_cast(static_cast>>(1) << i); + return ret; + } + else { + constexpr auto bits = + []() { + auto copy = (Max > Min ? Max - Min : Min - Max); // handle negative; + std::size_t count = 0; + do + ++count; + while (copy >>= 1); + return count; + }(); + std::array b{}; // 0 value reflected + for (std::size_t i = 0; i < bits; ++i) + b[i + 1] = static_cast(static_cast>>(1) << i); + return b; + } + } +#else + if constexpr (BitFlagEnum) { + constexpr std::size_t bits = sizeof(Enum) * CHAR_BIT; + std::array ret{}; // 0 value reflected + for (std::size_t i = 0; i < bits; ++i) + ret[i + 1] = static_cast(static_cast>>(1) << i); + return ret; + } +#endif + else { + static_assert(Min < Max, "enum_traits::min must be less than enum_traits::max"); + std::array array; + auto* const array_data = array.data(); + for (std::size_t i = 0, size = array.size(); i < size; ++i) + array_data[i] = static_cast(static_cast(i) + Min); + return array; + } +} +} // namespace enchantum::details + +#if defined(__clang__) + +#if __clang_major__ < 20 + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wenum-constexpr-conversion" +#endif + +#include +#include +#include +#include +#include + +namespace enchantum { +namespace details { + +#if __clang_major__ >= 20 + + template + inline constexpr bool is_valid_cast = false; + + template + inline constexpr bool is_valid_cast(V)>>> = true; + + template max_range = 1> + constexpr auto valid_cast_range() + { + if constexpr (max_range >= 0) { + if constexpr (max_range <= ENCHANTUM_MAX_RANGE) { + // this tests whether `static_cast`ing max_range is valid + // because C style enums stupidly is like a bit field + // `enum E { a,b,c,d = 3};` is like a bitfield `struct E { int val : 2;}` + // which means giving E.val a larger than 2 bit value is UB so is it for enums + // and gcc and msvc ignore this (for good) + // while clang makes it a subsituation failure which we can check for + // using std::inegral_constant makes sure this is a constant expression situation + // for SFINAE to occur + if constexpr (is_valid_cast) + return valid_cast_range(); + else + return max_range - 1; + } + else { + return max_range - 1; + } + } + else { + if constexpr (max_range >= ENCHANTUM_MIN_RANGE) { + // this tests whether `static_cast`ing max_range is valid + // because C style enums stupidly is like a bit field + // `enum E { a,b,c,d = 3};` is like a bitfield `struct E { int val : 2;}` + // which means giving E.val a larger than 2 bit value is UB so is it for enums + // and gcc and msvc ignore this (for good) + // while clang makes it a subsituation failure which we can check for + // using std::inegral_constant makes sure this is a constant expression situation + // for SFINAE to occur + if constexpr (is_valid_cast) + return valid_cast_range(); + else + return max_range / 2; + } + else { + return max_range / 2; + } + } + } +#else + template max_range = 1> + constexpr auto valid_cast_range() + { + if constexpr (max_range >= 0) + return ENCHANTUM_MAX_RANGE; + else + return ENCHANTUM_MIN_RANGE; + } +#endif + +} // namespace details + +template + requires SignedEnum && (!EnumFixedUnderlying) +struct enum_traits { + static constexpr auto max = details::valid_cast_range(); + static constexpr decltype(max) min = details::valid_cast_range(); +}; + +template + requires UnsignedEnum && (!EnumFixedUnderlying) +struct enum_traits { + static constexpr auto max = details::valid_cast_range(); + static constexpr decltype(max) min = 0; +}; + +namespace details { + + template + constexpr auto type_name_func() noexcept + { + // constexpr auto f() [with _ = Scoped] + //return __PRETTY_FUNCTION__; + constexpr auto funcname = string_view( + __PRETTY_FUNCTION__ + (sizeof("auto enchantum::details::type_name_func() [_ = ") - 1)); + // (sizeof("auto __cdecl enchantum::details::type_name_func<") - 1) + constexpr auto size = funcname.size() - (sizeof("]") - 1); + std::array ret; + auto* const ret_data = ret.data(); + const auto* const funcname_data = funcname.data(); + for (std::size_t i = 0; i < size; ++i) + ret_data[i] = funcname_data[i]; + return ret; + } + + template + inline constexpr auto type_name = type_name_func(); + + template + constexpr auto enum_in_array_name() noexcept + { + // constexpr auto f() [with auto _ = ( + //constexpr auto f() [Enum = (Scoped)0] + string_view s = __PRETTY_FUNCTION__ + (sizeof("auto enchantum::details::enum_in_array_name() [Enum = ") - 1); + s.remove_suffix(sizeof("]") - 1); + + if constexpr (ScopedEnum) { + if (s[s.size() - 2] == ')') { + s.remove_prefix(sizeof("(") - 1); + s.remove_suffix(sizeof(")0") - 1); + return s; + } + else { + return s.substr(0, s.rfind("::")); + } + } + else { + if (s[s.size() - 2] == ')') { + s.remove_prefix(sizeof("(") - 1); + s.remove_suffix(sizeof(")0") - 1); + } + if (const auto pos = s.rfind("::"); pos != s.npos) + return s.substr(0, pos); + return string_view(); + } + } + + template + constexpr auto var_name() noexcept + { + // "auto enchantum::details::var_name() [Vs = <(A)0, a, b, c, e, d, (A)6>]" +#define SZC(x) (sizeof(x) - 1) + constexpr auto funcsig_off = SZC("auto enchantum::details::var_name() [Vs = <"); + return string_view(__PRETTY_FUNCTION__ + funcsig_off, SZC(__PRETTY_FUNCTION__) - funcsig_off - SZC(">]")); +#undef SZC + } + + template + inline constexpr auto static_storage_for = Copy; + + template + constexpr auto reflect() noexcept + { + constexpr auto Min = enum_traits::min; + constexpr auto Max = enum_traits::max; + + constexpr auto elements = []() { + constexpr auto Array = details::generate_arrays(); + auto str = [Array](std::index_sequence) { + return details::var_name(); + }(std::make_index_sequence()); + + struct RetVal { + std::array pairs{}; + std::size_t total_string_length = 0; + std::size_t valid_count = 0; + } ret; + + std::size_t index = 0; + constexpr auto enum_in_array_name = details::enum_in_array_name(); + constexpr auto enum_in_array_len = enum_in_array_name.size(); + + // ((anonymous namespace)::A)0 + // (anonymous namespace)::a + + // this is needed to determine whether the above are cast expression if 2 braces are + // next to eachother then it is a cast but only for anonymoused namespaced enums + constexpr std::size_t index_check = !enum_in_array_name.empty() && enum_in_array_name.front() == '(' ? 1 : 0; + while (index < Array.size()) { + if (str[index_check] == '(') { + str.remove_prefix(sizeof("(") - 1 + enum_in_array_len + sizeof(")0") - 1); // there is atleast 1 base 10 digit + //if(!str.empty()) + // std::cout << "after str \"" << str << '"' << '\n'; + if (const auto commapos = str.find(','); commapos != str.npos) + str.remove_prefix(commapos + 2); + //std::cout << "strsize \"" << str.size() << '"' << '\n'; + } + else { + if constexpr (enum_in_array_len != 0) { + str.remove_prefix(enum_in_array_len + (sizeof("::") - 1)); + } + if constexpr (details::prefix_length_or_zero != 0) { + str.remove_prefix(details::prefix_length_or_zero); + } + const auto commapos = str.find(','); + + const auto name = str.substr(0, commapos); + + ret.pairs[ret.valid_count] = Pair{Array[index], name}; + ret.total_string_length += name.size() + ShouldNullTerminate; + + if (commapos != str.npos) + str.remove_prefix(commapos + 2); // skip comma and space + ++ret.valid_count; + } + ++index; + } + return ret; + }(); + + constexpr auto strings = [elements]() { + std::array strings; + for (std::size_t _i = 0, index = 0; _i < elements.valid_count; ++_i) { + const auto& [_, s] = elements.pairs[_i]; + for (std::size_t i = 0; i < s.size(); ++i) + strings[index++] = s[i]; + + if constexpr (ShouldNullTerminate) + strings[index++] = '\0'; + } + return strings; + }(); + + std::array ret; + constexpr const auto* str = static_storage_for.data(); + for (std::size_t i = 0, string_index = 0; i < elements.valid_count; ++i) { + const auto& [e, s] = elements.pairs[i]; + auto& [re, rs] = ret[i]; + re = e; + + rs = {str + string_index, str + string_index + s.size()}; + string_index += s.size() + ShouldNullTerminate; + } + return ret; + } + +} // namespace details + +//template +//constexpr std::size_t enum_count = details::enum_count; + +} // namespace enchantum + +#if __clang_major__ < 20 + #pragma clang diagnostic pop +#endif + +#elif defined(__GNUC__) || defined(__GNUG__) + + +#include +#include +#include +#include +#include + +namespace enchantum { + +namespace details { + + template + constexpr auto type_name_func() noexcept + { + // constexpr auto f() [with _ = Scoped] + //return __PRETTY_FUNCTION__; + constexpr auto funcname = string_view( + __PRETTY_FUNCTION__ + (sizeof("constexpr auto enchantum::details::type_name_func() [with _ = ") - 1)); + // (sizeof("auto __cdecl enchantum::details::type_name_func<") - 1) + constexpr auto size = funcname.size() - (sizeof("]") - 1); + std::array ret; + auto* const ret_data = ret.data(); + const auto* const funcname_data = funcname.data(); + for (std::size_t i = 0; i < size; ++i) + ret_data[i] = funcname_data[i]; + return ret; + } + + template + inline constexpr auto type_name = type_name_func(); + + template + constexpr auto enum_in_array_name() noexcept + { + // constexpr auto f() [with auto _ = ( + //constexpr auto f() [with auto _ = (Scoped)0] + string_view s = __PRETTY_FUNCTION__ + + sizeof("constexpr auto enchantum::details::enum_in_array_name() [with auto Enum = ") - 1; + s.remove_suffix(sizeof("]") - 1); + + if constexpr (ScopedEnum) { + if (s.front() == '(') { + s.remove_prefix(1); + s.remove_suffix(sizeof(")0") - 1); + return s; + } + else { + return s.substr(0, s.rfind("::")); + } + } + else { + if (s.front() == '(') { + s.remove_prefix(1); + s.remove_suffix(sizeof(")0") - 1); + } + if (const auto pos = s.rfind("::"); pos != s.npos) + return s.substr(0, pos); + return string_view(); + } + } + + template + constexpr auto length_of_enum_in_template_array_if_casting() noexcept + { + if constexpr (ScopedEnum) { + return details::enum_in_array_name().size(); + } + else { + constexpr auto s = enum_in_array_name().size(); + constexpr auto& tyname = type_name; + constexpr auto str = string_view(tyname.data(), tyname.size()); + if (constexpr auto pos = str.rfind("::"); pos != str.npos) { + return s + str.substr(pos).size(); + } + else { + return s + tyname.size(); + } + } + } + + template + constexpr auto var_name() noexcept + { + //constexpr auto f() [with auto _ = std::array{std::__array_traits::_Type{a, b, c, e, d, (E)6}}] +#define SZC(x) (sizeof(x) - 1) + constexpr std::size_t funcsig_off = SZC("constexpr auto enchantum::details::var_name() [with auto ...Vs = {"); + return std::string_view(__PRETTY_FUNCTION__ + funcsig_off, SZC(__PRETTY_FUNCTION__) - funcsig_off - SZC("}]")); + } + + template + inline constexpr auto static_storage_for = Copy; + + template + constexpr auto reflect() noexcept + { + constexpr auto Min = enum_traits::min; + constexpr auto Max = enum_traits::max; + + constexpr auto elements = []() { + constexpr auto length_of_enum_in_template_array_casting = details::length_of_enum_in_template_array_if_casting(); + constexpr auto Array = details::generate_arrays(); + auto str = [Array](std::index_sequence) { + return details::var_name(); + }(std::make_index_sequence()); + struct RetVal { + std::array pairs{}; + std::size_t total_string_length = 0; + std::size_t valid_count = 0; + } ret; + std::size_t index = 0; + constexpr auto enum_in_array_len = enum_in_array_name().size(); + while (index < Array.size()) { + if (str.front() == '(') { + str.remove_prefix(sizeof("(") - 1 + length_of_enum_in_template_array_casting + sizeof(")0") - + 1); // there is atleast 1 base 10 digit + //if(!str.empty()) + // std::cout << "after str \"" << str << '"' << '\n'; + + if (const auto commapos = str.find(','); commapos != str.npos) + str.remove_prefix(commapos + 2); + + //std::cout << "strsize \"" << str.size() << '"' << '\n'; + } + else { + if constexpr (enum_in_array_len != 0) + str.remove_prefix(enum_in_array_len + sizeof("::") - 1); + if constexpr (details::prefix_length_or_zero != 0) { + str.remove_prefix(details::prefix_length_or_zero); + } + const auto commapos = str.find(','); + + const auto name = str.substr(0, commapos); + + ret.pairs[ret.valid_count] = Pair{Array[index], name}; + ret.total_string_length += name.size() + ShouldNullTerminate; + + if (commapos != str.npos) + str.remove_prefix(commapos + 2); + ++ret.valid_count; + } + ++index; + } + return ret; + }(); + + constexpr auto strings = [elements]() { + std::array strings; + for (std::size_t _i = 0, index = 0; _i < elements.valid_count; ++_i) { + const auto& [_, s] = elements.pairs[_i]; + for (std::size_t i = 0; i < s.size(); ++i) + strings[index++] = s[i]; + + if constexpr (ShouldNullTerminate) + strings[index++] = '\0'; + } + return strings; + }(); + + std::array ret; + constexpr const auto* str = static_storage_for.data(); + for (std::size_t i = 0, string_index = 0; i < elements.valid_count; ++i) { + const auto& [e, s] = elements.pairs[i]; + auto& [re, rs] = ret[i]; + re = e; + + rs = {str + string_index, str + string_index + s.size()}; + string_index += s.size() + ShouldNullTerminate; + } + return ret; + } + +} // namespace details + +} // namespace enchantum +#elif defined(_MSC_VER) + + +#include +#include +#include +#include +#include + +namespace enchantum { + +namespace details { + +#define SZC(x) (sizeof(x) - 1) + template + constexpr auto type_name_func_size() noexcept + { + // (sizeof("auto __cdecl enchantum::details::type_name_func<") - 1) + return SZC(__FUNCSIG__) - SZC("auto __cdecl enchantum::details::type_name_func_size(void) noexcept"); + } + + template + constexpr auto enum_in_array_name() noexcept + { + string_view s = __FUNCSIG__ + sizeof("auto __cdecl enchantum::details::enum_in_array_name<") - 1; + s.remove_suffix(sizeof(">(void) noexcept") - 1); + + if constexpr (ScopedEnum) { + if (s.front() == '(') { + s.remove_prefix(sizeof("(enum ") - 1); + s.remove_suffix(sizeof(")0x0") - 1); + return s; + } + return s.substr(0, s.rfind("::")); + } + else { + if (s.front() == '(') { + s.remove_prefix(sizeof("(enum ") - 1); + s.remove_suffix(sizeof(")0x0") - 1); + } + if (const auto pos = s.rfind("::"); pos != s.npos) + return s.substr(0, pos); + return string_view(); + } + } + + template + constexpr auto var_name() noexcept + { + //auto __cdecl f{enum `anonymous-namespace'::UnscopedAnon + + using T = typename decltype(Array)::value_type; +#define SZC(x) (sizeof(x) - 1) + std::size_t funcsig_off = SZC("auto __cdecl enchantum::details::var_name(); + funcsig_off += type_name_len + SZC(","); + constexpr auto Size = Array.size(); + // clang-format off + funcsig_off += Size < 10 ? 1 + : Size < 100 ? 2 + : Size < 1000 ? 3 + : Size < 10000 ? 4 + : Size < 100000 ? 5 + : Size < 1000000 ? 6 + : Size < 10000000 ? 7 + : Size < 100000000 ? 8 + : Size < 1000000000 ? 9 + : 10; + // clang-format on + funcsig_off += SZC(">{enum ") + type_name_len; + return string_view(__FUNCSIG__ + funcsig_off, SZC(__FUNCSIG__) - funcsig_off - (sizeof("}>(void) noexcept") - 1)); + } +#undef SZC + + template + inline constexpr auto static_storage_for = Copy; + + template + constexpr auto get_elements() + { + constexpr auto type_name_len = details::type_name_func_size(); + + auto str = var_name(); + struct RetVal { + std::array pairs{}; + std::size_t total_string_length = 0; + std::size_t valid_count = 0; + } ret; + std::size_t index = 0; + constexpr auto enum_in_array_len = details::enum_in_array_name().size(); + while (index < Array.size()) { + if (str.front() == '(') { + str.remove_prefix(sizeof("(enum ") - 1 + type_name_len + sizeof(")0x0") - 1); // there is atleast 1 base 16 hex digit + + if (const auto commapos = str.find(','); commapos != str.npos) + str.remove_prefix(commapos + 1); + } + else { + if constexpr (enum_in_array_len != 0) + str.remove_prefix(enum_in_array_len + sizeof("::") - 1); + + if constexpr (details::prefix_length_or_zero != 0) + str.remove_prefix(details::prefix_length_or_zero); + + const auto commapos = str.find(','); + + const auto name = str.substr(0, commapos); + + ret.pairs[ret.valid_count] = Pair{Array[index], name}; + ret.total_string_length += name.size() + ShouldNullTerminate; + + if (commapos != str.npos) + str.remove_prefix(commapos + 1); + ++ret.valid_count; + } + ++index; + } + return ret; + } + + template + constexpr auto reflect() noexcept + { + constexpr auto Min = enum_traits::min; + constexpr auto Max = enum_traits::max; + + constexpr auto elements = details::get_elements(), ShouldNullTerminate>(); + + constexpr auto strings = [elements]() { + std::array strings; + for (std::size_t _i = 0, index = 0; _i < elements.valid_count; ++_i) { + const auto& [_, s] = elements.pairs[_i]; + for (std::size_t i = 0; i < s.size(); ++i) + strings[index++] = s[i]; + + if constexpr (ShouldNullTerminate) + strings[index++] = '\0'; + } + return strings; + }(); + + std::array ret; + constexpr const auto* str = static_storage_for.data(); + for (std::size_t i = 0, string_index = 0; i < elements.valid_count; ++i) { + const auto& [e, s] = elements.pairs[i]; + auto& [re, rs] = ret[i]; + re = e; + + rs = {str + string_index, str + string_index + s.size()}; + string_index += s.size() + ShouldNullTerminate; + } + + return ret; + } +} // namespace details + +} // namespace enchantum + +#endif + +#include +#include + +namespace enchantum { + +#ifdef __cpp_lib_to_underlying +using ::std::to_underlying; +#else +template +[[nodiscard]] constexpr auto to_underlying(const E e) noexcept +{ + return static_cast>(e); +} +#endif + +template, bool ShouldNullTerminate = true> +inline constexpr auto entries = details::reflect, Pair, ShouldNullTerminate>(); + +template +inline constexpr auto values = []() { + constexpr auto& enums = entries; + std::array ret; + for (std::size_t i = 0; i < ret.size(); ++i) + ret[i] = enums[i].first; + return ret; +}(); + +template +inline constexpr auto names = []() { + constexpr auto& enums = entries, NullTerminated>; + std::array ret; + for (std::size_t i = 0; i < ret.size(); ++i) + ret[i] = enums[i].second; + return ret; +}(); + +template +inline constexpr auto min = entries.front().first; + +template +inline constexpr auto max = entries.back().first; + +template +inline constexpr std::size_t count = entries.size(); + +} // namespace enchantum +#include +#include + +namespace enchantum { + +template +inline constexpr bool has_zero_flag = false; + +template +inline constexpr bool has_zero_flag = []() { + for (const auto v : values) + if (static_cast>(v) == 0) + return true; + return false; +}(); + +template +inline constexpr bool is_contiguous = false; + +template +inline constexpr bool is_contiguous = []() { + using T = std::underlying_type_t; + if constexpr (std::is_same_v) { + return true; + } + else { + constexpr auto& enums = entries; + for (std::size_t i = 0; i < enums.size() - 1; ++i) + if (T(enums[i].first) + 1 != T(enums[i + 1].first)) + return false; + return true; + } +}(); + +template +concept ContiguousEnum = Enum && is_contiguous; + +template +inline constexpr bool is_contiguous_bitflag = false; + +template +inline constexpr bool is_contiguous_bitflag = []() { + constexpr auto& enums = entries; + using T = std::underlying_type_t; + for (auto i = std::size_t{has_zero_flag}; i < enums.size() - 1; ++i) + if (T(enums[i].first) << 1 != T(enums[i + 1].first)) + return false; + return true; +}(); + +template +concept ContiguousBitFlagEnum = BitFlagEnum && is_contiguous_bitflag; + +template +[[nodiscard]] constexpr bool contains(const E value) noexcept +{ + for (const auto v : values) + if (v == value) + return true; + return false; +} + +template +[[nodiscard]] constexpr bool contains(const std::underlying_type_t value) noexcept +{ + return enchantum::contains(static_cast(value)); +} + +template +[[nodiscard]] constexpr bool contains(const string_view name) noexcept +{ + for (const auto& s : names) + if (s == name) + return true; + return false; +} + +template BinaryPredicate> +[[nodiscard]] constexpr bool contains(const string_view name, const BinaryPredicate binary_predicate) noexcept +{ + for (const auto& s : names) + if (binary_predicate(name, s)) + return true; + return false; +} + +template +[[nodiscard]] constexpr bool contains(const E value) noexcept +{ + using T = std::underlying_type_t; + return T(value) <= T(max) && T(value) >= T(min); +} + +namespace details { + template + struct index_to_enum_functor { + [[nodiscard]] constexpr optional operator()(const std::size_t index) const noexcept + { + optional ret; + if (index < values.size()) + ret.emplace(values[index]); + return ret; + } + }; + + struct enum_to_index_functor { + template + [[nodiscard]] constexpr optional operator()(const E e) const noexcept + { + if constexpr (ContiguousEnum) { + using T = std::underlying_type_t; + if (enchantum::contains(e)) + return optional(std::size_t(T(e) - T(min))); + } + else { + for (std::size_t i = 0; i < values.size(); ++i) + if (values[i] == e) + return optional(i); + } + return optional(); + } + }; + + template + struct cast_functor { + [[nodiscard]] constexpr optional operator()(const std::underlying_type_t value) const noexcept + { + optional a; // rvo not that it really matters + if (!enchantum::contains(value)) + return a; + a.emplace(static_cast(value)); + return a; + } + + [[nodiscard]] constexpr optional operator()(const string_view name) const noexcept + { + optional a; // rvo not that it really matters + for (const auto& [e, s] : entries) { + if (s == name) { + a.emplace(e); + return a; + } + } + return a; // nullopt + } + + template BinaryPred> + [[nodiscard]] constexpr optional operator()(const string_view name, const BinaryPred binary_predicate) const noexcept + { + optional a; // rvo not that it really matters + for (const auto& [e, s] : entries) { + if (binary_predicate(name, s)) { + a.emplace(e); + return a; + } + } + return a; + } + }; + +} // namespace details + +template +inline constexpr details::index_to_enum_functor index_to_enum{}; + +inline constexpr details::enum_to_index_functor enum_to_index{}; + +template +inline constexpr details::cast_functor cast{}; + +namespace details { + struct to_string_functor { + template + [[nodiscard]] constexpr string_view operator()(const E value) const noexcept + { + if (const auto i = enchantum::enum_to_index(value)) + return names[*i]; + return string_view(); + } + }; + +} // namespace details +inline constexpr details::to_string_functor to_string{}; + +} // namespace enchantum +#include +#include + +namespace enchantum { + +#if 0 +namespace details { + + template + constexpr auto cartesian_product() + { + constexpr auto size = []() { + std::size_t x = range; + std::size_t n = sets; + while (--n != 0) + x *= range; + return x; + }(); + + std::array, size> products{}; + std::array counter{}; + + for (auto& product : products) { + product = counter; + + ++counter.back(); + for (std::size_t i = counter.size() - 1; i != 0; i--) { + if (counter[i] != range) + break; + + counter[i] = 0; + ++counter[i - 1]; + } + } + return products; + } + +} // namespace details +#endif + +#if 0 +template Func> +constexpr auto visit(Func func, E e) +noexcept(std::is_nothrow_invocable_v) +{ + using Ret = decltype(func(e)); + + + return [&](std::index_sequence) { + if ((values[Idx] == enums)) + (func(std::integral_constant[Idx]> {}), ...); + }(std::make_index_sequence>()); +} +template Func> +constexpr auto visit(Func func, Enums... enums) noexcept(std::is_nothrow_invocable_v) +{ + using Ret = decltype(func(enums...)); + return [&](std::index_sequence) { + if ((values[Idx] == enums) && ...) + (func(std::integral_constant[Idx]> {}), ...); + }(std::make_index_sequence>()...); +} +#endif +namespace details { + + template + constexpr auto for_each(Func& f, std::index_sequence) + { + (void)(f(std::integral_constant[I]> {}), ...); + } + +} // namespace details + +template +constexpr void for_each(Func f) // intentional not const +{ + details::for_each(f, std::make_index_sequence>{}); +} +} // namespace enchantum + +#include +#include + +namespace enchantum { + +template +class array : public std::array> { +private: + using base = std::array>; +public: + using index_type = E; + using typename base::const_iterator; + using typename base::const_pointer; + using typename base::const_reference; + using typename base::const_reverse_iterator; + using typename base::difference_type; + using typename base::iterator; + using typename base::pointer; + using typename base::reference; + using typename base::reverse_iterator; + using typename base::size_type; + using typename base::value_type; + + using base::at; + using base::operator[]; + + [[nodiscard]] constexpr reference at(const E index) + { + if (const auto i = enchantum::enum_to_index(index)) + return operator[](*i); + ENCHANTUM_THROW(std::out_of_range("enchantum::array::at index out of range"), index); + } + + [[nodiscard]] constexpr const_reference at(const E index) const + { + if (const auto i = enchantum::enum_to_index(index)) + return operator[](*i); + ENCHANTUM_THROW(std::out_of_range("enchantum::array::at: index out of range"), index); + } + + [[nodiscard]] constexpr reference operator[](const E index) noexcept + { + return operator[](*enchantum::enum_to_index(index)); + } + + [[nodiscard]] constexpr const_reference operator[](const E index) const noexcept + { + return operator[](*enchantum::enum_to_index(index)); + } +}; + +} // namespace enchantum + +#ifndef ENCHANTUM_ALIAS_STRING + #include +#endif + +namespace enchantum { +#ifdef ENCHANTUM_ALIAS_STRING +ENCHANTUM_ALIAS_STRING; +#else +using ::std::string; +#endif +} // namespace enchantum + +namespace enchantum { + +template +inline constexpr E values_ors = [] { + E ret{}; + for (const auto val : values) + ret |= val; + return ret; +}(); + +template +[[nodiscard]] constexpr bool contains_bitflag(const std::underlying_type_t value) noexcept +{ + if (value == 0) + return has_zero_flag; + using T = std::underlying_type_t; + T valid_bits = 0; + + for (auto i = std::size_t{has_zero_flag}; i < count; ++i) { + const auto v = static_cast(values[i]); + if ((value & v) == v) + valid_bits |= v; + } + return valid_bits == value; +} + +template +[[nodiscard]] constexpr bool contains_bitflag(const E value) noexcept +{ + return enchantum::contains_bitflag(static_cast>(value)); +} + +template BinaryPred> +[[nodiscard]] constexpr bool contains_bitflag(const string_view s, const char sep, const BinaryPred binary_pred) noexcept +{ + std::size_t pos = 0; + for (std::size_t i = s.find(sep); i != s.npos; i = s.find(sep, pos)) { + if (!enchantum::contains(s.substr(pos, i - pos)), binary_pred) + return false; + pos = i + 1; + } + return enchantum::contains(s.substr(pos), binary_pred); +} + +template +[[nodiscard]] constexpr bool contains_bitflag(const string_view s, const char sep = '|') noexcept +{ + std::size_t pos = 0; + for (std::size_t i = s.find(sep); i != s.npos; i = s.find(sep, pos)) { + if (!enchantum::contains(s.substr(pos, i - pos))) + return false; + pos = i + 1; + } + return enchantum::contains(s.substr(pos)); +} + +template +[[nodiscard]] constexpr String to_string_bitflag(const E value, const char sep = '|') +{ + using T = std::underlying_type_t; + if constexpr (has_zero_flag) + if (static_cast(value) == 0) + return String(names[0]); + + String name; + T check_value = 0; + for (auto i = std::size_t{has_zero_flag}; i < count; ++i) { + const auto& [v, s] = entries[i]; + if (v == (value & v)) { + if (!name.empty()) + name.append(1, sep); // append separator if not the first value + name.append(s.data(), s.size()); // not using operator += since this may not be std::string_view always + check_value |= static_cast(v); + } + } + if (check_value == static_cast(value)) + return name; + return String{}; +} + +template BinaryPred> +[[nodiscard]] constexpr optional cast_bitflag(const string_view s, const char sep, const BinaryPred binary_pred) noexcept +{ + using T = std::underlying_type_t; + T check_value{}; + std::size_t pos = 0; + for (std::size_t i = s.find(sep); i != s.npos; i = s.find(sep, pos)) { + if (const auto v = enchantum::cast(s.substr(pos, i - pos),binary_pred)) + check_value |= static_cast(*v); + else + return optional(); + pos = i + 1; + } + + if (const auto v = enchantum::cast(s.substr(pos), binary_pred)) + return optional(static_cast(check_value | static_cast(*v))); + return optional(); +} + +template +[[nodiscard]] constexpr optional cast_bitflag(const string_view s, const char sep = '|') noexcept +{ + return enchantum::cast_bitflag(s, sep, [](const auto& a, const auto& b) { return a == b; }); +} + +template +[[nodiscard]] constexpr optional cast_bitflag(const E value) noexcept +{ + using T = std::underlying_type_t; + const auto raw_value = static_cast(value); + + if constexpr (has_zero_flag) + if (raw_value == 0) + return optional(E{}); + + T valid_bits{0}; + for (auto i = std::size_t{has_zero_flag}; i < count; ++i) { + auto v = static_cast(values[i]); + if ((raw_value & v) == v) + valid_bits |= v; + } + return valid_bits == raw_value ? optional(value) : optional(); +} + +} // namespace enchantum + +#include +/* +Note this header is an extremely easy way to cause ODR issues. + +class Flags { F1 = 1 << 0,F2 = 1<< 1}; +// **note I did not define any operators** + +enchantum::contains(Flags::F1); // considered a classical `Enum` concept + +using namespace enchantum::bitwise_operators; + +enchantum::contains(Flags::F1); // considered `BitFlagEnum` concept woops! ODR! + +*/ + +namespace enchantum::bitwise_operators { +template +[[nodiscard]] constexpr E operator~(E e) noexcept +{ + return static_cast(~static_cast>(e)); +} + +template +[[nodiscard]] constexpr E operator|(E a, E b) noexcept +{ + using T = std::underlying_type_t; + return static_cast(static_cast(a) | static_cast(b)); +} + +template +[[nodiscard]] constexpr E operator&(E a, E b) noexcept +{ + using T = std::underlying_type_t; + return static_cast(static_cast(a) & static_cast(b)); +} + +template +[[nodiscard]] constexpr E operator^(E a, E b) noexcept +{ + using T = std::underlying_type_t; + return static_cast(static_cast(a) ^ static_cast(b)); +} + +template +constexpr E& operator|=(E& a, E b) noexcept +{ + using T = std::underlying_type_t; + return a = static_cast(static_cast(a) | static_cast(b)); +} + +template +constexpr E& operator&=(E& a, E b) noexcept +{ + using T = std::underlying_type_t; + return a = static_cast(static_cast(a) & static_cast(b)); +} + +template +constexpr E& operator^=(E& a, E b) noexcept +{ + using T = std::underlying_type_t; + return a = static_cast(static_cast(a) ^ static_cast(b)); +} + +} // namespace enchantum::bitwise_operators + +#define ENCHANTUM_DEFINE_BITWISE_FOR(Enum) \ + [[nodiscard]] constexpr Enum operator&(Enum a, Enum b) noexcept \ + { \ + using T = std::underlying_type_t; \ + return static_cast(static_cast(a) & static_cast(b)); \ + } \ + [[nodiscard]] constexpr Enum operator|(Enum a, Enum b) noexcept \ + { \ + using T = std::underlying_type_t; \ + return static_cast(static_cast(a) | static_cast(b)); \ + } \ + [[nodiscard]] constexpr Enum operator^(Enum a, Enum b) noexcept \ + { \ + using T = std::underlying_type_t; \ + return static_cast(static_cast(a) ^ static_cast(b)); \ + } \ + constexpr Enum& operator&=(Enum& a, Enum b) noexcept { return a = a & b; } \ + constexpr Enum& operator|=(Enum& a, Enum b) noexcept { return a = a | b; } \ + constexpr Enum& operator^=(Enum& a, Enum b) noexcept { return a = a ^ b; } \ + [[nodiscard]] constexpr Enum operator~(Enum a) noexcept \ + { \ + return static_cast(~static_cast>(a)); \ + } + +#include +#include +#include + +namespace enchantum::istream_operators { +template + requires std::assignable_from +std::basic_istream& operator>>(std::basic_istream& is, E& value) +{ + std::basic_string s; + is >> s; + if (!is) + return is; + + if (const auto v = enchantum::cast(s)) + value = *v; + else + is.setstate(std::ios_base::failbit); + return is; +} + +} // namespace enchantum::istream_operators + +#include +#include + +namespace enchantum::ostream_operators { +template +std::basic_ostream& operator<<(std::basic_ostream& os, const E value) +{ + return os << enchantum::to_string(value); +} +} // namespace enchantum::ostream_operators + +namespace enchantum::iostream_operators { +using ::enchantum::istream_operators::operator>>; +using ::enchantum::ostream_operators::operator<<; +} // namespace enchantum::iostream_operators + +#include + +namespace enchantum { +namespace details { + template + struct next_value_functor { + template + [[nodiscard]] constexpr optional operator()(const E value, const std::ptrdiff_t n = 1) const noexcept + { + if (!enchantum::contains(value)) + return optional{}; + + const auto index = static_cast(*enchantum::enum_to_index(value)) + (n * N); + if (index >= 0 && index < static_cast(count)) + return optional{values[static_cast(index)]}; + return optional{}; + } + }; + + template + struct next_value_circular_functor { + template + [[nodiscard]] constexpr E operator()(const E value, const std::ptrdiff_t n = 1) const noexcept + { + ENCHANTUM_ASSERT(enchantum::contains(value), "next/prev_value_circular requires 'value' to be a valid enum member", value); + const auto i = static_cast(*enchantum::enum_to_index(value)); + constexpr auto count = static_cast(enchantum::count); + return values[static_cast(((i + (n * N)) % count + count) % count)]; // handles wrap around and negative n + } + }; +} // namespace details + +inline constexpr details::next_value_functor<1> next_value{}; +inline constexpr details::next_value_functor<-1> prev_value{}; +inline constexpr details::next_value_circular_functor<1> next_value_circular{}; +inline constexpr details::next_value_circular_functor<-1> prev_value_circular{}; + +} // namespace enchantum + +#if __has_include() + + +#include +#include + +template +struct fmt::formatter { + template + static constexpr auto parse(ParseContext& ctx) + { + return ctx.begin(); + } + + template + static auto format(const E e, FmtContext& ctx) + { + if constexpr (enchantum::BitFlagEnum) + return fmt::format_to(ctx.out(), "{}", enchantum::to_string_bitflag(e)); + else + return fmt::format_to(ctx.out(), "{}", enchantum::to_string(e)); + } +}; +#elif __has_include() + + +#include +#include + +template +struct std::formatter { + template + static constexpr auto parse(ParseContext& ctx) + { + return ctx.begin(); + } + + template + static auto format(const E e, FmtContext& ctx) + { + if constexpr (enchantum::BitFlagEnum) + return std::format_to(ctx.out(), "{}", enchantum::to_string_bitflag(e)); + else + return std::format_to(ctx.out(), "{}", enchantum::to_string(e)); + } +}; +#endif \ No newline at end of file From 083214320a1fa07ffadbcb552bf92e8c21ed720e Mon Sep 17 00:00:00 2001 From: ZXShady <153229951+ZXShady@users.noreply.github.com> Date: Wed, 28 May 2025 11:20:17 +0200 Subject: [PATCH 2/8] Simplify code --- include/rfl/enums.hpp | 16 ++++---- include/rfl/internal/enums/Names.hpp | 4 +- include/rfl/internal/enums/get_enum_names.hpp | 37 +++---------------- include/rfl/internal/enums/is_flag_enum.hpp | 3 +- include/rfl/internal/enums/is_scoped_enum.hpp | 19 ---------- 5 files changed, 16 insertions(+), 63 deletions(-) delete mode 100644 include/rfl/internal/enums/is_scoped_enum.hpp diff --git a/include/rfl/enums.hpp b/include/rfl/enums.hpp index b7a889b3..afc152fd 100644 --- a/include/rfl/enums.hpp +++ b/include/rfl/enums.hpp @@ -7,25 +7,25 @@ #include "internal/enums/StringConverter.hpp" #include "internal/enums/get_enum_names.hpp" #include "internal/enums/is_flag_enum.hpp" -#include "internal/enums/is_scoped_enum.hpp" +#include "thirdparty/enchantum.hpp" namespace rfl { // Converts an enum value to a string. -template +template std::string enum_to_string(EnumType _enum) { return rfl::internal::enums::StringConverter::enum_to_string(_enum); } // Converts a string to a value of the given enum type. -template +template rfl::Result string_to_enum(const std::string& _str) { return rfl::internal::enums::StringConverter::string_to_enum(_str); } // Returns a named tuple mapping names of enumerators of the given enum type to // their values. -template +template auto get_enumerators() { constexpr auto names = internal::enums::get_enum_names< EnumType, internal::enums::is_flag_enum>(); @@ -34,7 +34,7 @@ auto get_enumerators() { // Returns a named tuple mapping names of enumerators of the given enum type to // their underlying values. -template +template auto get_underlying_enumerators() { constexpr auto names = internal::enums::get_enum_names< EnumType, internal::enums::is_flag_enum>(); @@ -43,7 +43,7 @@ auto get_underlying_enumerators() { // Returns an std::array containing pairs of enumerator names (as // std::string_view) and values. -template +template constexpr auto get_enumerator_array() { constexpr auto names = internal::enums::get_enum_names< EnumType, internal::enums::is_flag_enum>(); @@ -52,7 +52,7 @@ constexpr auto get_enumerator_array() { // Returns an std::array containing pairs of enumerator names (as // std::string_view) and underlying values. -template +template constexpr auto get_underlying_enumerator_array() { constexpr auto names = internal::enums::get_enum_names< EnumType, internal::enums::is_flag_enum>(); @@ -60,7 +60,7 @@ constexpr auto get_underlying_enumerator_array() { } // Returns the range of the given enum type as a pair of the minimum and maximum -template +template constexpr auto get_enum_range() { return std::make_pair( internal::enums::get_range_min consteval auto get_enum_names() { - // constexpr auto max = get_range_max(); - // constexpr auto min = get_range_min(); - // constexpr auto range_size = max - min + 1; - // - // static_assert(range_size > 0, - // "enum_range requires a valid range size. Ensure that max is " - // "greater than min."); - // - // using EmptyNames = Names, 0, _is_flag>; - - constexpr auto convert = []() { - constexpr auto a = enchantum::entries; - std::array, a.size()> b; - for (std::size_t i = 0; i < a.size(); ++i) { - b[i].first = a[i].first; - b[i].second = {a[i].second.data(), a[i].second.size()}; - } - return b; - }(); - - // return Names, sizeof...(entries), _is_flag, - // entries.first...>{}; - - return [convert](std::index_sequence) { + return [](std::index_sequence) { + constexpr auto& entries = enchantum::entries; constexpr auto to_str_lit = [](const char* name, std::index_sequence) { return StringLiteral{name[Js]...}; }; return Names{})...>, - convert.size(), _is_flag, convert[Is].first...>{}; + entries[Is].second.data(), + std::make_index_sequence{})...>, + entries.size(), _is_flag, entries[Is].first...>{}; }(std::make_index_sequence>{}); } } // namespace rfl::internal::enums diff --git a/include/rfl/internal/enums/is_flag_enum.hpp b/include/rfl/internal/enums/is_flag_enum.hpp index 660a0fe0..c22192c4 100644 --- a/include/rfl/internal/enums/is_flag_enum.hpp +++ b/include/rfl/internal/enums/is_flag_enum.hpp @@ -3,7 +3,6 @@ #include -#include "is_scoped_enum.hpp" #include "../../thirdparty/enchantum.hpp" template requires requires(E e) { @@ -17,7 +16,7 @@ namespace enums { template concept is_flag_enum = - is_scoped_enum && enchantum::BitFlagEnum; + enchantum::ScopedEnum && enchantum::BitFlagEnum; } // namespace enums diff --git a/include/rfl/internal/enums/is_scoped_enum.hpp b/include/rfl/internal/enums/is_scoped_enum.hpp deleted file mode 100644 index 6880d4c7..00000000 --- a/include/rfl/internal/enums/is_scoped_enum.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef RFL_INTERNAL_ENUMS_IS_SCOPED_ENUM_HPP_ -#define RFL_INTERNAL_ENUMS_IS_SCOPED_ENUM_HPP_ - -#include -#include -#include "../../thirdparty/enchantum.hpp" - -namespace rfl { -namespace internal { -namespace enums { - -template -concept is_scoped_enum = enchantum::ScopedEnum; - -} // namespace enums -} // namespace internal -} // namespace rfl - -#endif From d8f2ae0dc98c0f0c3b1f57b440e5fdc46cbd3ded Mon Sep 17 00:00:00 2001 From: ZXShady <153229951+ZXShady@users.noreply.github.com> Date: Wed, 28 May 2025 13:05:05 +0200 Subject: [PATCH 3/8] Remove StringConverter.hpp --- include/rfl/enums.hpp | 90 +++++++++---- include/rfl/internal/enums/Names.hpp | 4 +- .../rfl/internal/enums/StringConverter.hpp | 119 ------------------ include/rfl/internal/enums/get_enum_names.hpp | 20 +-- include/rfl/internal/enums/is_flag_enum.hpp | 26 ---- include/rfl/parsing/Parser_default.hpp | 17 ++- 6 files changed, 88 insertions(+), 188 deletions(-) delete mode 100644 include/rfl/internal/enums/StringConverter.hpp delete mode 100644 include/rfl/internal/enums/is_flag_enum.hpp diff --git a/include/rfl/enums.hpp b/include/rfl/enums.hpp index afc152fd..a9ec619c 100644 --- a/include/rfl/enums.hpp +++ b/include/rfl/enums.hpp @@ -4,69 +4,111 @@ #include #include "Result.hpp" -#include "internal/enums/StringConverter.hpp" #include "internal/enums/get_enum_names.hpp" -#include "internal/enums/is_flag_enum.hpp" #include "thirdparty/enchantum.hpp" +#include "internal/strings/strings.hpp" namespace rfl { -// Converts an enum value to a string. template -std::string enum_to_string(EnumType _enum) { - return rfl::internal::enums::StringConverter::enum_to_string(_enum); +std::string enum_to_string(const EnumType _enum) { + const auto to_string_or_number = [](const EnumType e) { + const auto s = enchantum::to_string(e); + return s.empty() ? std::to_string( + static_cast>(e)) + : std::string(s); + }; + + if constexpr (enchantum::is_bitflag) { + // Iterates through the enum bit by bit and matches it against the flags. + using T = std::underlying_type_t; + auto val = static_cast(_enum); + int i = 0; + std::vector flags; + while (val != 0) { + const auto bit = val & static_cast(1); + if (bit == 1) { + auto str = + to_string_or_number(static_cast(static_cast(1) << i)); + flags.emplace_back(std::move(str)); + } + ++i; + val >>= 1; + } + return internal::strings::join("|", flags); + } else { + return to_string_or_number(_enum); + } } // Converts a string to a value of the given enum type. template -rfl::Result string_to_enum(const std::string& _str) { - return rfl::internal::enums::StringConverter::string_to_enum(_str); +Result string_to_enum(const std::string& _str) { + const auto cast_numbers_or_names = [](const std::string& name) -> Result { + const auto r = enchantum::cast(name); + if (r) return *r; + try { + return static_cast(std::stoi(name)); + } catch (std::exception& exp) { + return error(exp.what()); + } + }; + + if constexpr (enchantum::is_bitflag) { + using T = std::underlying_type_t; + const auto split = internal::strings::split(_str, "|"); + auto res = static_cast(0); + for (const auto& s : split) { + const auto r = cast_numbers_or_names(s); + if (r) { + res |= static_cast(*r); + } else { + return r; + } + } + return static_cast(res); + } else { + return cast_numbers_or_names(_str); + } } // Returns a named tuple mapping names of enumerators of the given enum type to // their values. template auto get_enumerators() { - constexpr auto names = internal::enums::get_enum_names< - EnumType, internal::enums::is_flag_enum>(); - return internal::enums::names_to_enumerator_named_tuple(names); + return internal::enums::names_to_enumerator_named_tuple( + internal::enums::get_enum_names()); } // Returns a named tuple mapping names of enumerators of the given enum type to // their underlying values. template auto get_underlying_enumerators() { - constexpr auto names = internal::enums::get_enum_names< - EnumType, internal::enums::is_flag_enum>(); - return internal::enums::names_to_underlying_enumerator_named_tuple(names); + return internal::enums::names_to_underlying_enumerator_named_tuple( + internal::enums::get_enum_names()); } // Returns an std::array containing pairs of enumerator names (as // std::string_view) and values. template constexpr auto get_enumerator_array() { - constexpr auto names = internal::enums::get_enum_names< - EnumType, internal::enums::is_flag_enum>(); - return internal::enums::names_to_enumerator_array(names); + return internal::enums::names_to_enumerator_array( + internal::enums::get_enum_names()); } // Returns an std::array containing pairs of enumerator names (as // std::string_view) and underlying values. template constexpr auto get_underlying_enumerator_array() { - constexpr auto names = internal::enums::get_enum_names< - EnumType, internal::enums::is_flag_enum>(); - return internal::enums::names_to_underlying_enumerator_array(names); + return internal::enums::names_to_underlying_enumerator_array( + internal::enums::get_enum_names()); } // Returns the range of the given enum type as a pair of the minimum and maximum template constexpr auto get_enum_range() { - return std::make_pair( - internal::enums::get_range_min>(), - internal::enums::get_range_max< - EnumType, internal::enums::is_flag_enum>()); + return std::make_pair(internal::enums::get_range_min(), + internal::enums::get_range_max()); } } // namespace rfl diff --git a/include/rfl/internal/enums/Names.hpp b/include/rfl/internal/enums/Names.hpp index d98eb84b..014fd03d 100644 --- a/include/rfl/internal/enums/Names.hpp +++ b/include/rfl/internal/enums/Names.hpp @@ -2,7 +2,7 @@ #define RFL_INTERNAL_ENUMS_NAMES_HPP_ // Enum values must be greater than or equal to RFL_ENUM_RANGE_MIN. -// By default, RFL_ENUM_RANGE_MIN is set to -256. +// By default, RFL_ENUM_RANGE_MIN is set to 0. // To change the default minimum range for all enum types, redefine the macro // RFL_ENUM_RANGE_MIN. #if !defined(RFL_ENUM_RANGE_MIN) @@ -10,7 +10,7 @@ #endif // Enum values must be less than or equal to RFL_ENUM_RANGE_MAX. -// By default, RFL_ENUM_RANGE_MAX is set to 256. +// By default, RFL_ENUM_RANGE_MAX is set to 127. // To change the default maximum range for all enum types, redefine the macro // RFL_ENUM_RANGE_MAX. #if !defined(RFL_ENUM_RANGE_MAX) diff --git a/include/rfl/internal/enums/StringConverter.hpp b/include/rfl/internal/enums/StringConverter.hpp deleted file mode 100644 index 19149002..00000000 --- a/include/rfl/internal/enums/StringConverter.hpp +++ /dev/null @@ -1,119 +0,0 @@ -#ifndef RFL_INTERNAL_ENUMS_STRINGCONVERTER_HPP_ -#define RFL_INTERNAL_ENUMS_STRINGCONVERTER_HPP_ - -#include -#include -#include -#include -#include - -#include "../../Result.hpp" -#include "../../internal/strings/strings.hpp" -#include "../../type_name_t.hpp" -#include "../../thirdparty/enchantum.hpp" -#include "get_enum_names.hpp" -#include "is_flag_enum.hpp" - -namespace rfl ::internal ::enums { - -template -class StringConverter { - public: - static constexpr bool is_flag_enum_ = is_flag_enum; - - static constexpr auto names_ = get_enum_names(); - - using NamesLiteral = typename decltype(names_)::Literal; - - public: - /// Transform an enum to a matching string. - static std::string enum_to_string(const EnumType _enum) { - if constexpr (is_flag_enum_) { - return flag_enum_to_string(_enum); - } else { - return enum_to_single_string(_enum); - } - } - - /// Transforms a string to the matching enum. - static Result string_to_enum(const std::string& _str) { - static_assert(names_.size != 0, - "No enum could be identified. Please choose enum values " - "between 0 to 127 or for flag enums choose 1,2,4,8,16,..."); - if constexpr (is_flag_enum_) { - return string_to_flag_enum(_str); - } else { - return single_string_to_enum(_str); - } - } - - private: - /// Iterates through the enum bit by bit and matches it against the flags. - static std::string flag_enum_to_string(const EnumType _e) { - using T = std::underlying_type_t; - auto val = static_cast(_e); - int i = 0; - std::vector flags; - while (val != 0) { - const auto bit = val & static_cast(1); - if (bit == 1) { - auto str = enum_to_single_string( - static_cast(static_cast(1) << i)); - flags.emplace_back(std::move(str)); - } - ++i; - val >>= 1; - } - return strings::join("|", flags); - } - - /// This assumes that _enum can be exactly matched to one of the names and - /// does not have to be combined using |. - static std::string enum_to_single_string(const EnumType _enum) { - const auto s = enchantum::to_string(_enum); - return s.empty() ? std::to_string( - static_cast>(_enum)) - : std::string(s); - } - - /// Finds the enum matching the literal. - static EnumType literal_to_enum(const NamesLiteral _lit) noexcept { - return names_.enums_[_lit.value()]; - } - - /// This assumes that _enum can be exactly matched to one of the names and - /// does not have to be combined using |. - static Result single_string_to_enum(const std::string& _str) { - const auto r = enchantum::cast(_str); - if (r) - return *r; - - try { - return static_cast(std::stoi(_str)); - } catch (std::exception& exp) { - return error(exp.what()); - } - } - - /// Only relevant if this is a flag enum - combines the different matches - /// using |. - static Result string_to_flag_enum( - const std::string& _str) noexcept { - using T = std::underlying_type_t; - const auto split = strings::split(_str, "|"); - auto res = static_cast(0); - for (const auto& s : split) { - const auto r = single_string_to_enum(s); - if (r) { - res |= static_cast(*r); - } else { - return r; - } - } - return static_cast(res); - } -}; - -} // namespace rfl::internal::enums - -#endif diff --git a/include/rfl/internal/enums/get_enum_names.hpp b/include/rfl/internal/enums/get_enum_names.hpp index facc3d4c..e308c995 100644 --- a/include/rfl/internal/enums/get_enum_names.hpp +++ b/include/rfl/internal/enums/get_enum_names.hpp @@ -4,10 +4,7 @@ #include #include #include - -#if __has_include() -#include -#endif +#include #include "../../Literal.hpp" #include "../../thirdparty/enchantum.hpp" @@ -37,9 +34,16 @@ // use a scoped integer, static_cast(some_integer_value) will always be // defined. +template + requires requires(E e) { + { e | e } -> std::same_as; + } +constexpr inline bool enchantum::is_bitflag = true; + + namespace rfl::internal::enums { -template +template > consteval auto get_range_min() { using U = std::underlying_type_t; if constexpr (_is_flag) { @@ -50,7 +54,7 @@ consteval auto get_range_min() { range_min::value); } } -template +template > consteval auto get_range_max() { using U = std::underlying_type_t; if constexpr (_is_flag) { @@ -66,7 +70,7 @@ consteval auto get_range_max() { } } -template +template consteval auto get_enum_names() { return [](std::index_sequence) { constexpr auto& entries = enchantum::entries; @@ -78,7 +82,7 @@ consteval auto get_enum_names() { Literal{})...>, - entries.size(), _is_flag, entries[Is].first...>{}; + entries.size(), enchantum::is_bitflag, entries[Is].first...>{}; }(std::make_index_sequence>{}); } } // namespace rfl::internal::enums diff --git a/include/rfl/internal/enums/is_flag_enum.hpp b/include/rfl/internal/enums/is_flag_enum.hpp deleted file mode 100644 index c22192c4..00000000 --- a/include/rfl/internal/enums/is_flag_enum.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef RFL_INTERNAL_ENUMS_IS_FLAG_ENUM_HPP_ -#define RFL_INTERNAL_ENUMS_IS_FLAG_ENUM_HPP_ - -#include - -#include "../../thirdparty/enchantum.hpp" -template -requires requires(E e) { - { e | e } -> std::same_as; -} -constexpr inline bool enchantum::is_bitflag = true; - -namespace rfl { -namespace internal { -namespace enums { - -template -concept is_flag_enum = - enchantum::ScopedEnum && enchantum::BitFlagEnum; - - -} // namespace enums -} // namespace internal -} // namespace rfl - -#endif diff --git a/include/rfl/parsing/Parser_default.hpp b/include/rfl/parsing/Parser_default.hpp index c4e4f3e1..bc98ac3a 100644 --- a/include/rfl/parsing/Parser_default.hpp +++ b/include/rfl/parsing/Parser_default.hpp @@ -7,8 +7,8 @@ #include "../Result.hpp" #include "../always_false.hpp" +#include "../enums.hpp" #include "../from_named_tuple.hpp" -#include "../internal/enums/StringConverter.hpp" #include "../internal/has_reflection_method_v.hpp" #include "../internal/has_reflection_type_v.hpp" #include "../internal/has_reflector.hpp" @@ -89,9 +89,8 @@ struct Parser { return static_cast( *_r.template to_basic_type>(_var)); } else { - using StringConverter = internal::enums::StringConverter; return _r.template to_basic_type(_var).and_then( - StringConverter::string_to_enum); + rfl::string_to_enum); } } else { @@ -133,8 +132,7 @@ struct Parser { const auto val = static_cast>(_var); ParentType::add_value(_w, val, _parent); } else { - using StringConverter = internal::enums::StringConverter; - const auto str = StringConverter::enum_to_string(_var); + const auto str = rfl::enum_to_string(_var); ParentType::add_value(_w, str, _parent); } @@ -214,15 +212,16 @@ struct Parser { static schema::Type make_enum( std::map* _definitions) { using Type = schema::Type; - using S = internal::enums::StringConverter; if constexpr (ProcessorsType::underlying_enums_ || schemaful::IsSchemafulReader) { return Type{Type::Integer{}}; - } else if constexpr (S::is_flag_enum_) { + } else if constexpr (enchantum::is_bitflag) { return Type{Type::String{}}; } else { - return Parser::to_schema( - _definitions); + return Parser< + R, W, + typename decltype(internal::enums::get_enum_names())::Literal, + ProcessorsType>::to_schema(_definitions); } } From 1124d41f56eea27c651ee93d4e2955c8fca417e9 Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Thu, 29 May 2025 11:03:34 +0200 Subject: [PATCH 4/8] Made sure that the range tests pass --- include/rfl/config.hpp | 19 +++++++ include/rfl/enums.hpp | 9 ++-- include/rfl/internal/enums/Names.hpp | 49 +++++-------------- include/rfl/internal/enums/get_enum_names.hpp | 46 +++++++++++++++-- include/rfl/internal/enums/range_defined.hpp | 19 +++++++ tests/generic/test_enum_range_min_max.cpp | 9 +++- tests/generic/test_enum_range_size.cpp | 6 +-- 7 files changed, 107 insertions(+), 50 deletions(-) create mode 100644 include/rfl/config.hpp create mode 100644 include/rfl/internal/enums/range_defined.hpp diff --git a/include/rfl/config.hpp b/include/rfl/config.hpp new file mode 100644 index 00000000..9526f207 --- /dev/null +++ b/include/rfl/config.hpp @@ -0,0 +1,19 @@ +#ifndef RFL_CONFIG_HPP_ +#define RFL_CONFIG_HPP_ + +namespace rfl::config { + +// To specify a different range for a particular enum type, specialize the +// enum_range template for that enum type. +template +struct enum_range { + // In your template specialization, uncomment these two lines and replace them + // with the values of your choice. + // static constexpr int min = ...; + // static constexpr int max = ...; +}; + +} // namespace rfl::config + +#endif + diff --git a/include/rfl/enums.hpp b/include/rfl/enums.hpp index a9ec619c..3c897cb0 100644 --- a/include/rfl/enums.hpp +++ b/include/rfl/enums.hpp @@ -5,8 +5,8 @@ #include "Result.hpp" #include "internal/enums/get_enum_names.hpp" -#include "thirdparty/enchantum.hpp" #include "internal/strings/strings.hpp" +#include "thirdparty/enchantum.hpp" namespace rfl { @@ -44,7 +44,8 @@ std::string enum_to_string(const EnumType _enum) { // Converts a string to a value of the given enum type. template Result string_to_enum(const std::string& _str) { - const auto cast_numbers_or_names = [](const std::string& name) -> Result { + const auto cast_numbers_or_names = + [](const std::string& name) -> Result { const auto r = enchantum::cast(name); if (r) return *r; try { @@ -107,8 +108,8 @@ constexpr auto get_underlying_enumerator_array() { // Returns the range of the given enum type as a pair of the minimum and maximum template constexpr auto get_enum_range() { - return std::make_pair(internal::enums::get_range_min(), - internal::enums::get_range_max()); + return std::make_pair(enchantum::enum_traits::min, + enchantum::enum_traits::max); } } // namespace rfl diff --git a/include/rfl/internal/enums/Names.hpp b/include/rfl/internal/enums/Names.hpp index 014fd03d..8118f872 100644 --- a/include/rfl/internal/enums/Names.hpp +++ b/include/rfl/internal/enums/Names.hpp @@ -2,7 +2,7 @@ #define RFL_INTERNAL_ENUMS_NAMES_HPP_ // Enum values must be greater than or equal to RFL_ENUM_RANGE_MIN. -// By default, RFL_ENUM_RANGE_MIN is set to 0. +// By default, RFL_ENUM_RANGE_MIN is set to -256. // To change the default minimum range for all enum types, redefine the macro // RFL_ENUM_RANGE_MIN. #if !defined(RFL_ENUM_RANGE_MIN) @@ -10,7 +10,7 @@ #endif // Enum values must be less than or equal to RFL_ENUM_RANGE_MAX. -// By default, RFL_ENUM_RANGE_MAX is set to 127. +// By default, RFL_ENUM_RANGE_MAX is set to 256. // To change the default maximum range for all enum types, redefine the macro // RFL_ENUM_RANGE_MAX. #if !defined(RFL_ENUM_RANGE_MAX) @@ -35,26 +35,13 @@ #include #include "../../Literal.hpp" +#include "../../config.hpp" #include "../../define_literal.hpp" #include "../../make_named_tuple.hpp" +#include "../../thirdparty/enchantum.hpp" #include "../StringLiteral.hpp" -namespace rfl { - - -namespace config { - -// To specify a different range for a particular enum type, specialize the -// enum_range template for that enum type. -template -struct enum_range { - static constexpr int min = RFL_ENUM_RANGE_MIN; - static constexpr int max = RFL_ENUM_RANGE_MAX; -}; -} // namespace config - -namespace internal { -namespace enums { +namespace rfl::internal::enums { static_assert(RFL_ENUM_RANGE_MAX > RFL_ENUM_RANGE_MIN, "RFL_ENUM_RANGE_MAX must be greater than RFL_ENUM_RANGE_MIN."); @@ -74,9 +61,9 @@ template struct range_max::max)>> : std::integral_constant::max), config::enum_range::max> {}; - template struct Names { + auto... _enums> +struct Names { /// Contains a collection of enums as compile-time strings. using Literal = LiteralType; @@ -84,28 +71,17 @@ template {_enums...}; - - static_assert(N == 0 || LiteralType::size() == N, - "Size of literal and enum do not match."); - - template - using AddOneType = std::conditional_t< - N == 0, Names, - Names, N + 1, - _is_flag, _enums..., _new_enum>>; + constexpr static auto enums_ = std::array{_enums...}; }; - template consteval auto operator|( Names, Names) { using CombinedLiteral = define_literal_t; - return Names{}; + return Names{}; } template >(_enums))...}; } -} // namespace enums -} // namespace internal -} // namespace rfl +} // namespace rfl::internal::enums #endif - diff --git a/include/rfl/internal/enums/get_enum_names.hpp b/include/rfl/internal/enums/get_enum_names.hpp index e308c995..423ec2de 100644 --- a/include/rfl/internal/enums/get_enum_names.hpp +++ b/include/rfl/internal/enums/get_enum_names.hpp @@ -1,14 +1,14 @@ #ifndef RFL_INTERNAL_ENUMS_GET_ENUM_NAMES_HPP_ #define RFL_INTERNAL_ENUMS_GET_ENUM_NAMES_HPP_ +#include #include #include #include -#include #include "../../Literal.hpp" -#include "../../thirdparty/enchantum.hpp" #include "Names.hpp" +#include "range_defined.hpp" // https://en.cppreference.com/w/cpp/language/static_cast: @@ -40,6 +40,45 @@ template } constexpr inline bool enchantum::is_bitflag = true; +// Specialize the enchantum EnumTraits further, so rfl::config::enum_range +// works. +namespace enchantum { + +template + requires rfl::internal::enums::range_defined +struct enum_traits { + static constexpr std::size_t prefix_length = 0; + + static constexpr auto min = rfl::config::enum_range::min; + static constexpr auto max = rfl::config::enum_range::max; +}; + +template + requires rfl::internal::enums::range_defined +struct enum_traits { + static constexpr std::size_t prefix_length = 0; + + static constexpr auto min = rfl::config::enum_range::min; + static constexpr auto max = rfl::config::enum_range::max; +}; + +template + requires SignedEnum && + (!EnumFixedUnderlying) && rfl::internal::enums::range_defined +struct enum_traits { + static constexpr auto min = rfl::config::enum_range::min; + static constexpr auto max = rfl::config::enum_range::max; +}; + +template + requires UnsignedEnum && + (!EnumFixedUnderlying) && rfl::internal::enums::range_defined +struct enum_traits { + static constexpr auto min = rfl::config::enum_range::min; + static constexpr auto max = rfl::config::enum_range::max; +}; + +} // namespace enchantum namespace rfl::internal::enums { @@ -82,7 +121,8 @@ consteval auto get_enum_names() { Literal{})...>, - entries.size(), enchantum::is_bitflag, entries[Is].first...>{}; + entries.size(), enchantum::is_bitflag, + entries[Is].first...>{}; }(std::make_index_sequence>{}); } } // namespace rfl::internal::enums diff --git a/include/rfl/internal/enums/range_defined.hpp b/include/rfl/internal/enums/range_defined.hpp new file mode 100644 index 00000000..ec6ba5f3 --- /dev/null +++ b/include/rfl/internal/enums/range_defined.hpp @@ -0,0 +1,19 @@ +#ifndef RFL_INTERNAL_ENUMS_RANGE_DEFINED_HPP_ +#define RFL_INTERNAL_ENUMS_RANGE_DEFINED_HPP_ + +#include + +#include "../../config.hpp" + +namespace rfl::internal::enums { + +template +concept range_defined = requires { + { config::enum_range::min }; + + { config::enum_range::max }; +}; + +} // namespace rfl::internal::enums + +#endif diff --git a/tests/generic/test_enum_range_min_max.cpp b/tests/generic/test_enum_range_min_max.cpp index 16221a74..732a7171 100644 --- a/tests/generic/test_enum_range_min_max.cpp +++ b/tests/generic/test_enum_range_min_max.cpp @@ -14,19 +14,24 @@ enum class LineColor : uint16_t { yellow = 200, purple = 300, orange = 400 }; // Define the range of the LineColor enum template <> struct rfl::config::enum_range { - static constexpr int min = 200; + static constexpr int min = 190; static constexpr int max = 400; }; namespace test_enum_range_min_max { TEST(generic, test_enum_range_min_max) { + static_assert(!rfl::internal::enums::range_defined, + "Range should not be defined."); + static_assert(rfl::internal::enums::range_defined, + "Range should be defined."); + auto [inner_min, inner_max] = rfl::get_enum_range(); EXPECT_EQ(inner_min, RFL_ENUM_RANGE_MIN); EXPECT_EQ(inner_max, RFL_ENUM_RANGE_MAX); auto [line_min, line_max] = rfl::get_enum_range(); - EXPECT_EQ(line_min, 200); + EXPECT_EQ(line_min, 190); EXPECT_EQ(line_max, 400); } diff --git a/tests/generic/test_enum_range_size.cpp b/tests/generic/test_enum_range_size.cpp index 284ce294..46652097 100644 --- a/tests/generic/test_enum_range_size.cpp +++ b/tests/generic/test_enum_range_size.cpp @@ -8,7 +8,7 @@ namespace test_enum_range_size { enum class InnerColor { none = -128, red = -50, green = 0, blue = 128 }; enum class LineColor { yellow = 200, purple = 300, orange = 400 }; -enum class OneColor {black}; +enum class OneColor { black }; } // namespace test_enum_range_size @@ -23,7 +23,7 @@ struct rfl::config::enum_range { template <> struct rfl::config::enum_range { static constexpr int min = 0; - static constexpr int max = 0; + static constexpr int max = 1; }; namespace test_enum_range_size { @@ -34,4 +34,4 @@ TEST(generic, test_enum_range_size) { EXPECT_EQ(rfl::get_enumerator_array().size(), 1); } -} // namespace test_enum_range_size \ No newline at end of file +} // namespace test_enum_range_size From dc85a45a1197ce6df321291728d1e4bc02776517 Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Thu, 29 May 2025 11:15:58 +0200 Subject: [PATCH 5/8] Code cleanup --- include/rfl/internal/enums/Names.hpp | 54 ------------------- include/rfl/internal/enums/get_enum_names.hpp | 54 +++++++++---------- 2 files changed, 27 insertions(+), 81 deletions(-) diff --git a/include/rfl/internal/enums/Names.hpp b/include/rfl/internal/enums/Names.hpp index 8118f872..32a500e6 100644 --- a/include/rfl/internal/enums/Names.hpp +++ b/include/rfl/internal/enums/Names.hpp @@ -1,32 +1,6 @@ #ifndef RFL_INTERNAL_ENUMS_NAMES_HPP_ #define RFL_INTERNAL_ENUMS_NAMES_HPP_ -// Enum values must be greater than or equal to RFL_ENUM_RANGE_MIN. -// By default, RFL_ENUM_RANGE_MIN is set to -256. -// To change the default minimum range for all enum types, redefine the macro -// RFL_ENUM_RANGE_MIN. -#if !defined(RFL_ENUM_RANGE_MIN) -#define RFL_ENUM_RANGE_MIN -256 -#endif - -// Enum values must be less than or equal to RFL_ENUM_RANGE_MAX. -// By default, RFL_ENUM_RANGE_MAX is set to 256. -// To change the default maximum range for all enum types, redefine the macro -// RFL_ENUM_RANGE_MAX. -#if !defined(RFL_ENUM_RANGE_MAX) -#define RFL_ENUM_RANGE_MAX 256 -#endif - -#ifdef ENCHANTUM_MIN_RANGE -#undef ENCHANTUM_MIN_RANGE -#endif -#define ENCHANTUM_MIN_RANGE RFL_ENUM_RANGE_MIN - -#ifdef ENCHANTUM_MAX_RANGE -#undef ENCHANTUM_MAX_RANGE -#endif -#define ENCHANTUM_MAX_RANGE RFL_ENUM_RANGE_MAX - #include #include #include @@ -43,24 +17,6 @@ namespace rfl::internal::enums { -static_assert(RFL_ENUM_RANGE_MAX > RFL_ENUM_RANGE_MIN, - "RFL_ENUM_RANGE_MAX must be greater than RFL_ENUM_RANGE_MIN."); - -template -struct range_min : std::integral_constant {}; - -template -struct range_min::min)>> - : std::integral_constant::min), - config::enum_range::min> {}; - -template -struct range_max : std::integral_constant {}; - -template -struct range_max::max)>> - : std::integral_constant::max), - config::enum_range::max> {}; template struct Names { @@ -74,16 +30,6 @@ struct Names { constexpr static auto enums_ = std::array{_enums...}; }; -template -consteval auto operator|( - Names, - Names) { - using CombinedLiteral = define_literal_t; - return Names{}; -} - template auto names_to_enumerator_named_tuple( diff --git a/include/rfl/internal/enums/get_enum_names.hpp b/include/rfl/internal/enums/get_enum_names.hpp index 423ec2de..5d825ba6 100644 --- a/include/rfl/internal/enums/get_enum_names.hpp +++ b/include/rfl/internal/enums/get_enum_names.hpp @@ -1,12 +1,39 @@ #ifndef RFL_INTERNAL_ENUMS_GET_ENUM_NAMES_HPP_ #define RFL_INTERNAL_ENUMS_GET_ENUM_NAMES_HPP_ +// Enum values must be greater than or equal to RFL_ENUM_RANGE_MIN. +// By default, RFL_ENUM_RANGE_MIN is set to -256. +// To change the default minimum range for all enum types, redefine the macro +// RFL_ENUM_RANGE_MIN. +#if !defined(RFL_ENUM_RANGE_MIN) +#define RFL_ENUM_RANGE_MIN -256 +#endif + +// Enum values must be less than or equal to RFL_ENUM_RANGE_MAX. +// By default, RFL_ENUM_RANGE_MAX is set to 256. +// To change the default maximum range for all enum types, redefine the macro +// RFL_ENUM_RANGE_MAX. +#if !defined(RFL_ENUM_RANGE_MAX) +#define RFL_ENUM_RANGE_MAX 256 +#endif + +#ifdef ENCHANTUM_MIN_RANGE +#undef ENCHANTUM_MIN_RANGE +#endif +#define ENCHANTUM_MIN_RANGE RFL_ENUM_RANGE_MIN + +#ifdef ENCHANTUM_MAX_RANGE +#undef ENCHANTUM_MAX_RANGE +#endif +#define ENCHANTUM_MAX_RANGE RFL_ENUM_RANGE_MAX + #include #include #include #include #include "../../Literal.hpp" +#include "../../thirdparty/enchantum.hpp" #include "Names.hpp" #include "range_defined.hpp" @@ -82,33 +109,6 @@ struct enum_traits { namespace rfl::internal::enums { -template > -consteval auto get_range_min() { - using U = std::underlying_type_t; - if constexpr (_is_flag) { - return 0; - } else { - return std::max(static_cast::value)>( - std::numeric_limits::min()), - range_min::value); - } -} -template > -consteval auto get_range_max() { - using U = std::underlying_type_t; - if constexpr (_is_flag) { - if constexpr (std::is_signed_v) { - return (sizeof(U) * 8 - 2); - } else { - return (sizeof(U) * 8 - 1); - } - } else { - return std::min(static_cast::value)>( - std::numeric_limits::max()), - range_max::value); - } -} - template consteval auto get_enum_names() { return [](std::index_sequence) { From 267f890ccef81450cf42a16f590d86d9e86e385d Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Thu, 29 May 2025 11:23:17 +0200 Subject: [PATCH 6/8] Added test for unscoped enum --- tests/generic/test_enum.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/generic/test_enum.cpp b/tests/generic/test_enum.cpp index 1d92f4a5..c183fe70 100644 --- a/tests/generic/test_enum.cpp +++ b/tests/generic/test_enum.cpp @@ -8,14 +8,17 @@ namespace test_enum { enum class Color { red, green, blue, yellow }; +enum Location { left, centre, right }; // Unscoped enum struct Circle { float radius; Color color; + Location location; }; TEST(generic, test_enum) { - const auto circle = Circle{.radius = 2.0, .color = Color::green}; + const auto circle = + Circle{.radius = 2.0, .color = Color::green, .location = centre}; write_and_read(circle); } From 4db94de0cf5647f9f336f311e7f95c52abd1fe9a Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Thu, 29 May 2025 11:33:28 +0200 Subject: [PATCH 7/8] Updated the documentation --- README.md | 2 ++ docs/enums.md | 15 +++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e056eaeb..3907a55c 100644 --- a/README.md +++ b/README.md @@ -584,3 +584,5 @@ reflect-cpp is released under the MIT License. Refer to the LICENSE file for det reflect-cpp includes [YYJSON](https://github.com/ibireme/yyjson), the fastest JSON library currently in existence. YYJSON is written by YaoYuan and also released under the MIT License. reflect-cpp includes [compile-time-regular-expressions](https://github.com/hanickadot/compile-time-regular-expressions). CTRE is written by Hana Dusíková and released under the Apache-2.0 License with LLVM exceptions. + +reflect-cpp includes [enchantum](https://github.com/ZXShady/enchantum/tree/main). enchantum is written by ZXShady and also released under the MIT License. diff --git a/docs/enums.md b/docs/enums.md index 9cdf8734..b2280f0a 100644 --- a/docs/enums.md +++ b/docs/enums.md @@ -1,6 +1,6 @@ # Enums -reflect-cpp supports scoped enumerations. They can either come in the form of normal enumerations or flag enums. +reflect-cpp supports both scoped and unscoped enumerations as long as they are named. They can either come in the form of normal enumerations or flag enums. ## Normal enumerations @@ -27,21 +27,24 @@ This results in the following JSON string: However, some limitations apply: -1. They must be scoped enumerations. +1. They cannot be unnamed enumerations. ```cpp - /// OK - scoped enumeration + // OK - scoped enumeration enum class Color1 { red, green, blue, yellow }; - /// OK - scoped enumeration + // OK - scoped enumeration enum struct Color2 { red, green, blue, yellow }; - /// unsupported - unscoped enumerations + // OK - unscoped enumeration enum Color3 { red, green, blue, yellow }; + + /// Unsupported: Anonymous enumeration + enum { red, green, blue, yellow }; ``` 2. Enum values must be in the range `[RFL_ENUM_RANGE_MIN, RFL_ENUM_RANGE_MAX]`. If the range is not specified, the - default range is `[0, 127]`. + default range is `[-256, 256]`. - You can specify a custom range for the all enum values by defining `RFL_ENUM_RANGE_MIN` and `RFL_ENUM_RANGE_MAX` before including the reflect-cpp header: From 80fcf06e6b462530650eb25758585f2a32778144 Mon Sep 17 00:00:00 2001 From: ZXShady <153229951+ZXShady@users.noreply.github.com> Date: Thu, 29 May 2025 14:17:29 +0200 Subject: [PATCH 8/8] Use multiple headers --- include/rfl/enums.hpp | 3 +- include/rfl/internal/enums/Names.hpp | 2 +- include/rfl/internal/enums/get_enum_names.hpp | 2 +- include/rfl/thirdparty/enchantum.hpp | 1495 ----------------- .../rfl/thirdparty/enchantum/algorithms.hpp | 82 + include/rfl/thirdparty/enchantum/all.hpp | 20 + include/rfl/thirdparty/enchantum/array.hpp | 54 + include/rfl/thirdparty/enchantum/bitflags.hpp | 149 ++ include/rfl/thirdparty/enchantum/bitset.hpp | 109 ++ .../enchantum/bitwise_operators.hpp | 93 + include/rfl/thirdparty/enchantum/common.hpp | 98 ++ .../enchantum/details/enchantum_clang.hpp | 259 +++ .../enchantum/details/enchantum_gcc.hpp | 180 ++ .../enchantum/details/enchantum_msvc.hpp | 161 ++ .../enchantum/details/format_util.hpp | 20 + .../enchantum/details/generate_arrays.hpp | 63 + .../thirdparty/enchantum/details/optional.hpp | 19 + .../enchantum/details/string_view.hpp | 20 + .../rfl/thirdparty/enchantum/enchantum.hpp | 199 +++ include/rfl/thirdparty/enchantum/entries.hpp | 59 + .../rfl/thirdparty/enchantum/fmt_format.hpp | 15 + include/rfl/thirdparty/enchantum/iostream.hpp | 8 + include/rfl/thirdparty/enchantum/istream.hpp | 32 + .../rfl/thirdparty/enchantum/next_value.hpp | 44 + include/rfl/thirdparty/enchantum/ostream.hpp | 14 + .../rfl/thirdparty/enchantum/std_format.hpp | 17 + 26 files changed, 1719 insertions(+), 1498 deletions(-) delete mode 100644 include/rfl/thirdparty/enchantum.hpp create mode 100644 include/rfl/thirdparty/enchantum/algorithms.hpp create mode 100644 include/rfl/thirdparty/enchantum/all.hpp create mode 100644 include/rfl/thirdparty/enchantum/array.hpp create mode 100644 include/rfl/thirdparty/enchantum/bitflags.hpp create mode 100644 include/rfl/thirdparty/enchantum/bitset.hpp create mode 100644 include/rfl/thirdparty/enchantum/bitwise_operators.hpp create mode 100644 include/rfl/thirdparty/enchantum/common.hpp create mode 100644 include/rfl/thirdparty/enchantum/details/enchantum_clang.hpp create mode 100644 include/rfl/thirdparty/enchantum/details/enchantum_gcc.hpp create mode 100644 include/rfl/thirdparty/enchantum/details/enchantum_msvc.hpp create mode 100644 include/rfl/thirdparty/enchantum/details/format_util.hpp create mode 100644 include/rfl/thirdparty/enchantum/details/generate_arrays.hpp create mode 100644 include/rfl/thirdparty/enchantum/details/optional.hpp create mode 100644 include/rfl/thirdparty/enchantum/details/string_view.hpp create mode 100644 include/rfl/thirdparty/enchantum/enchantum.hpp create mode 100644 include/rfl/thirdparty/enchantum/entries.hpp create mode 100644 include/rfl/thirdparty/enchantum/fmt_format.hpp create mode 100644 include/rfl/thirdparty/enchantum/iostream.hpp create mode 100644 include/rfl/thirdparty/enchantum/istream.hpp create mode 100644 include/rfl/thirdparty/enchantum/next_value.hpp create mode 100644 include/rfl/thirdparty/enchantum/ostream.hpp create mode 100644 include/rfl/thirdparty/enchantum/std_format.hpp diff --git a/include/rfl/enums.hpp b/include/rfl/enums.hpp index 3c897cb0..f2cab910 100644 --- a/include/rfl/enums.hpp +++ b/include/rfl/enums.hpp @@ -6,7 +6,8 @@ #include "Result.hpp" #include "internal/enums/get_enum_names.hpp" #include "internal/strings/strings.hpp" -#include "thirdparty/enchantum.hpp" +#include "thirdparty/enchantum/enchantum.hpp" +#include "thirdparty/enchantum/bitflags.hpp" namespace rfl { diff --git a/include/rfl/internal/enums/Names.hpp b/include/rfl/internal/enums/Names.hpp index 32a500e6..16a3d18b 100644 --- a/include/rfl/internal/enums/Names.hpp +++ b/include/rfl/internal/enums/Names.hpp @@ -12,7 +12,7 @@ #include "../../config.hpp" #include "../../define_literal.hpp" #include "../../make_named_tuple.hpp" -#include "../../thirdparty/enchantum.hpp" +#include "../../thirdparty/enchantum/enchantum.hpp" #include "../StringLiteral.hpp" namespace rfl::internal::enums { diff --git a/include/rfl/internal/enums/get_enum_names.hpp b/include/rfl/internal/enums/get_enum_names.hpp index 5d825ba6..724837ee 100644 --- a/include/rfl/internal/enums/get_enum_names.hpp +++ b/include/rfl/internal/enums/get_enum_names.hpp @@ -33,7 +33,7 @@ #include #include "../../Literal.hpp" -#include "../../thirdparty/enchantum.hpp" +#include "../../thirdparty/enchantum/enchantum.hpp" #include "Names.hpp" #include "range_defined.hpp" diff --git a/include/rfl/thirdparty/enchantum.hpp b/include/rfl/thirdparty/enchantum.hpp deleted file mode 100644 index 0c445349..00000000 --- a/include/rfl/thirdparty/enchantum.hpp +++ /dev/null @@ -1,1495 +0,0 @@ -#pragma once - -#ifndef ENCHANTUM_ALIAS_OPTIONAL - #include -#endif - -namespace enchantum { -#ifdef ENCHANTUM_ALIAS_OPTIONAL -ENCHANTUM_ALIAS_OPTIONAL; -#else -using ::std::optional; -#endif - -} // namespace enchantum - -#ifndef ENCHANTUM_ALIAS_STRING_VIEW - #include -#endif - -namespace enchantum { -#ifdef ENCHANTUM_ALIAS_STRING_VIEW -ENCHANTUM_ALIAS_STRING_VIEW; -#else -using ::std::string_view; -#endif - -} // namespace enchantum - -#include -#include -#include -#include - -#ifndef ENCHANTUM_ASSERT - #include -// clang-format off - #define ENCHANTUM_ASSERT(cond, msg, ...) assert(cond && msg) -// clang-format on -#endif - -#ifndef ENCHANTUM_THROW - // additional info such as local variables are here - #define ENCHANTUM_THROW(exception, ...) throw exception -#endif - -#ifndef ENCHANTUM_MAX_RANGE - #define ENCHANTUM_MAX_RANGE 256 -#endif -#ifndef ENCHANTUM_MIN_RANGE - #define ENCHANTUM_MIN_RANGE (-ENCHANTUM_MAX_RANGE) -#endif - -namespace enchantum { - -template -concept Enum = std::is_enum_v; - -template -concept SignedEnum = Enum && std::signed_integral>; - -template -concept UnsignedEnum = Enum && !SignedEnum; - -template -concept ScopedEnum = Enum && (!std::is_convertible_v>); - -template -concept UnscopedEnum = Enum && !ScopedEnum; - -template -concept EnumOfUnderlying = Enum && std::same_as, Underlying>; - -template -inline constexpr bool is_bitflag = requires(E e) { - requires std::same_as || std::same_as; - { ~e } -> std::same_as; - { e | e } -> std::same_as; - { e &= e } -> std::same_as; - { e |= e } -> std::same_as; -}; - -template -concept BitFlagEnum = Enum && is_bitflag; - -template -concept EnumFixedUnderlying = Enum && requires { T{0}; }; - -template -struct enum_traits; - -template -struct enum_traits { -private: - using U = std::underlying_type_t; - using L = std::numeric_limits; -public: - static constexpr std::size_t prefix_length = 0; - - static constexpr auto min = (L::min)() > ENCHANTUM_MIN_RANGE ? (L::min)() : ENCHANTUM_MIN_RANGE; - static constexpr auto max = (L::max)() < ENCHANTUM_MAX_RANGE ? (L::max)() : ENCHANTUM_MAX_RANGE; -}; - -template -struct enum_traits { -private: - using T = std::underlying_type_t; - using L = std::numeric_limits; -public: - static constexpr std::size_t prefix_length = 0; - - static constexpr auto min = []() { - if constexpr (std::is_same_v) - return false; - else - return (ENCHANTUM_MIN_RANGE) < 0 ? 0 : (ENCHANTUM_MIN_RANGE); - }(); - static constexpr auto max = []() { - if constexpr (std::is_same_v) - return true; - else - return (L::max)() < (ENCHANTUM_MAX_RANGE) ? (L::max)() : (ENCHANTUM_MAX_RANGE); - }(); -}; - -} // namespace enchantum -#include -#include -#include -#include - -namespace enchantum::details { - -template -inline constexpr std::size_t prefix_length_or_zero = 0; - -template -inline constexpr auto prefix_length_or_zero::prefix_length)> = std::size_t{ - enum_traits::prefix_length}; - -template -constexpr auto generate_arrays() -{ -#if defined __clang__ && __clang_major__ >= 20 - if constexpr (BitFlagEnum) { - if constexpr (EnumFixedUnderlying) { - constexpr std::size_t bits = sizeof(Enum) * CHAR_BIT; - std::array ret{}; // 0 value reflected - for (std::size_t i = 0; i < bits; ++i) - ret[i + 1] = static_cast(static_cast>>(1) << i); - return ret; - } - else { - constexpr auto bits = - []() { - auto copy = (Max > Min ? Max - Min : Min - Max); // handle negative; - std::size_t count = 0; - do - ++count; - while (copy >>= 1); - return count; - }(); - std::array b{}; // 0 value reflected - for (std::size_t i = 0; i < bits; ++i) - b[i + 1] = static_cast(static_cast>>(1) << i); - return b; - } - } -#else - if constexpr (BitFlagEnum) { - constexpr std::size_t bits = sizeof(Enum) * CHAR_BIT; - std::array ret{}; // 0 value reflected - for (std::size_t i = 0; i < bits; ++i) - ret[i + 1] = static_cast(static_cast>>(1) << i); - return ret; - } -#endif - else { - static_assert(Min < Max, "enum_traits::min must be less than enum_traits::max"); - std::array array; - auto* const array_data = array.data(); - for (std::size_t i = 0, size = array.size(); i < size; ++i) - array_data[i] = static_cast(static_cast(i) + Min); - return array; - } -} -} // namespace enchantum::details - -#if defined(__clang__) - -#if __clang_major__ < 20 - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wenum-constexpr-conversion" -#endif - -#include -#include -#include -#include -#include - -namespace enchantum { -namespace details { - -#if __clang_major__ >= 20 - - template - inline constexpr bool is_valid_cast = false; - - template - inline constexpr bool is_valid_cast(V)>>> = true; - - template max_range = 1> - constexpr auto valid_cast_range() - { - if constexpr (max_range >= 0) { - if constexpr (max_range <= ENCHANTUM_MAX_RANGE) { - // this tests whether `static_cast`ing max_range is valid - // because C style enums stupidly is like a bit field - // `enum E { a,b,c,d = 3};` is like a bitfield `struct E { int val : 2;}` - // which means giving E.val a larger than 2 bit value is UB so is it for enums - // and gcc and msvc ignore this (for good) - // while clang makes it a subsituation failure which we can check for - // using std::inegral_constant makes sure this is a constant expression situation - // for SFINAE to occur - if constexpr (is_valid_cast) - return valid_cast_range(); - else - return max_range - 1; - } - else { - return max_range - 1; - } - } - else { - if constexpr (max_range >= ENCHANTUM_MIN_RANGE) { - // this tests whether `static_cast`ing max_range is valid - // because C style enums stupidly is like a bit field - // `enum E { a,b,c,d = 3};` is like a bitfield `struct E { int val : 2;}` - // which means giving E.val a larger than 2 bit value is UB so is it for enums - // and gcc and msvc ignore this (for good) - // while clang makes it a subsituation failure which we can check for - // using std::inegral_constant makes sure this is a constant expression situation - // for SFINAE to occur - if constexpr (is_valid_cast) - return valid_cast_range(); - else - return max_range / 2; - } - else { - return max_range / 2; - } - } - } -#else - template max_range = 1> - constexpr auto valid_cast_range() - { - if constexpr (max_range >= 0) - return ENCHANTUM_MAX_RANGE; - else - return ENCHANTUM_MIN_RANGE; - } -#endif - -} // namespace details - -template - requires SignedEnum && (!EnumFixedUnderlying) -struct enum_traits { - static constexpr auto max = details::valid_cast_range(); - static constexpr decltype(max) min = details::valid_cast_range(); -}; - -template - requires UnsignedEnum && (!EnumFixedUnderlying) -struct enum_traits { - static constexpr auto max = details::valid_cast_range(); - static constexpr decltype(max) min = 0; -}; - -namespace details { - - template - constexpr auto type_name_func() noexcept - { - // constexpr auto f() [with _ = Scoped] - //return __PRETTY_FUNCTION__; - constexpr auto funcname = string_view( - __PRETTY_FUNCTION__ + (sizeof("auto enchantum::details::type_name_func() [_ = ") - 1)); - // (sizeof("auto __cdecl enchantum::details::type_name_func<") - 1) - constexpr auto size = funcname.size() - (sizeof("]") - 1); - std::array ret; - auto* const ret_data = ret.data(); - const auto* const funcname_data = funcname.data(); - for (std::size_t i = 0; i < size; ++i) - ret_data[i] = funcname_data[i]; - return ret; - } - - template - inline constexpr auto type_name = type_name_func(); - - template - constexpr auto enum_in_array_name() noexcept - { - // constexpr auto f() [with auto _ = ( - //constexpr auto f() [Enum = (Scoped)0] - string_view s = __PRETTY_FUNCTION__ + (sizeof("auto enchantum::details::enum_in_array_name() [Enum = ") - 1); - s.remove_suffix(sizeof("]") - 1); - - if constexpr (ScopedEnum) { - if (s[s.size() - 2] == ')') { - s.remove_prefix(sizeof("(") - 1); - s.remove_suffix(sizeof(")0") - 1); - return s; - } - else { - return s.substr(0, s.rfind("::")); - } - } - else { - if (s[s.size() - 2] == ')') { - s.remove_prefix(sizeof("(") - 1); - s.remove_suffix(sizeof(")0") - 1); - } - if (const auto pos = s.rfind("::"); pos != s.npos) - return s.substr(0, pos); - return string_view(); - } - } - - template - constexpr auto var_name() noexcept - { - // "auto enchantum::details::var_name() [Vs = <(A)0, a, b, c, e, d, (A)6>]" -#define SZC(x) (sizeof(x) - 1) - constexpr auto funcsig_off = SZC("auto enchantum::details::var_name() [Vs = <"); - return string_view(__PRETTY_FUNCTION__ + funcsig_off, SZC(__PRETTY_FUNCTION__) - funcsig_off - SZC(">]")); -#undef SZC - } - - template - inline constexpr auto static_storage_for = Copy; - - template - constexpr auto reflect() noexcept - { - constexpr auto Min = enum_traits::min; - constexpr auto Max = enum_traits::max; - - constexpr auto elements = []() { - constexpr auto Array = details::generate_arrays(); - auto str = [Array](std::index_sequence) { - return details::var_name(); - }(std::make_index_sequence()); - - struct RetVal { - std::array pairs{}; - std::size_t total_string_length = 0; - std::size_t valid_count = 0; - } ret; - - std::size_t index = 0; - constexpr auto enum_in_array_name = details::enum_in_array_name(); - constexpr auto enum_in_array_len = enum_in_array_name.size(); - - // ((anonymous namespace)::A)0 - // (anonymous namespace)::a - - // this is needed to determine whether the above are cast expression if 2 braces are - // next to eachother then it is a cast but only for anonymoused namespaced enums - constexpr std::size_t index_check = !enum_in_array_name.empty() && enum_in_array_name.front() == '(' ? 1 : 0; - while (index < Array.size()) { - if (str[index_check] == '(') { - str.remove_prefix(sizeof("(") - 1 + enum_in_array_len + sizeof(")0") - 1); // there is atleast 1 base 10 digit - //if(!str.empty()) - // std::cout << "after str \"" << str << '"' << '\n'; - if (const auto commapos = str.find(','); commapos != str.npos) - str.remove_prefix(commapos + 2); - //std::cout << "strsize \"" << str.size() << '"' << '\n'; - } - else { - if constexpr (enum_in_array_len != 0) { - str.remove_prefix(enum_in_array_len + (sizeof("::") - 1)); - } - if constexpr (details::prefix_length_or_zero != 0) { - str.remove_prefix(details::prefix_length_or_zero); - } - const auto commapos = str.find(','); - - const auto name = str.substr(0, commapos); - - ret.pairs[ret.valid_count] = Pair{Array[index], name}; - ret.total_string_length += name.size() + ShouldNullTerminate; - - if (commapos != str.npos) - str.remove_prefix(commapos + 2); // skip comma and space - ++ret.valid_count; - } - ++index; - } - return ret; - }(); - - constexpr auto strings = [elements]() { - std::array strings; - for (std::size_t _i = 0, index = 0; _i < elements.valid_count; ++_i) { - const auto& [_, s] = elements.pairs[_i]; - for (std::size_t i = 0; i < s.size(); ++i) - strings[index++] = s[i]; - - if constexpr (ShouldNullTerminate) - strings[index++] = '\0'; - } - return strings; - }(); - - std::array ret; - constexpr const auto* str = static_storage_for.data(); - for (std::size_t i = 0, string_index = 0; i < elements.valid_count; ++i) { - const auto& [e, s] = elements.pairs[i]; - auto& [re, rs] = ret[i]; - re = e; - - rs = {str + string_index, str + string_index + s.size()}; - string_index += s.size() + ShouldNullTerminate; - } - return ret; - } - -} // namespace details - -//template -//constexpr std::size_t enum_count = details::enum_count; - -} // namespace enchantum - -#if __clang_major__ < 20 - #pragma clang diagnostic pop -#endif - -#elif defined(__GNUC__) || defined(__GNUG__) - - -#include -#include -#include -#include -#include - -namespace enchantum { - -namespace details { - - template - constexpr auto type_name_func() noexcept - { - // constexpr auto f() [with _ = Scoped] - //return __PRETTY_FUNCTION__; - constexpr auto funcname = string_view( - __PRETTY_FUNCTION__ + (sizeof("constexpr auto enchantum::details::type_name_func() [with _ = ") - 1)); - // (sizeof("auto __cdecl enchantum::details::type_name_func<") - 1) - constexpr auto size = funcname.size() - (sizeof("]") - 1); - std::array ret; - auto* const ret_data = ret.data(); - const auto* const funcname_data = funcname.data(); - for (std::size_t i = 0; i < size; ++i) - ret_data[i] = funcname_data[i]; - return ret; - } - - template - inline constexpr auto type_name = type_name_func(); - - template - constexpr auto enum_in_array_name() noexcept - { - // constexpr auto f() [with auto _ = ( - //constexpr auto f() [with auto _ = (Scoped)0] - string_view s = __PRETTY_FUNCTION__ + - sizeof("constexpr auto enchantum::details::enum_in_array_name() [with auto Enum = ") - 1; - s.remove_suffix(sizeof("]") - 1); - - if constexpr (ScopedEnum) { - if (s.front() == '(') { - s.remove_prefix(1); - s.remove_suffix(sizeof(")0") - 1); - return s; - } - else { - return s.substr(0, s.rfind("::")); - } - } - else { - if (s.front() == '(') { - s.remove_prefix(1); - s.remove_suffix(sizeof(")0") - 1); - } - if (const auto pos = s.rfind("::"); pos != s.npos) - return s.substr(0, pos); - return string_view(); - } - } - - template - constexpr auto length_of_enum_in_template_array_if_casting() noexcept - { - if constexpr (ScopedEnum) { - return details::enum_in_array_name().size(); - } - else { - constexpr auto s = enum_in_array_name().size(); - constexpr auto& tyname = type_name; - constexpr auto str = string_view(tyname.data(), tyname.size()); - if (constexpr auto pos = str.rfind("::"); pos != str.npos) { - return s + str.substr(pos).size(); - } - else { - return s + tyname.size(); - } - } - } - - template - constexpr auto var_name() noexcept - { - //constexpr auto f() [with auto _ = std::array{std::__array_traits::_Type{a, b, c, e, d, (E)6}}] -#define SZC(x) (sizeof(x) - 1) - constexpr std::size_t funcsig_off = SZC("constexpr auto enchantum::details::var_name() [with auto ...Vs = {"); - return std::string_view(__PRETTY_FUNCTION__ + funcsig_off, SZC(__PRETTY_FUNCTION__) - funcsig_off - SZC("}]")); - } - - template - inline constexpr auto static_storage_for = Copy; - - template - constexpr auto reflect() noexcept - { - constexpr auto Min = enum_traits::min; - constexpr auto Max = enum_traits::max; - - constexpr auto elements = []() { - constexpr auto length_of_enum_in_template_array_casting = details::length_of_enum_in_template_array_if_casting(); - constexpr auto Array = details::generate_arrays(); - auto str = [Array](std::index_sequence) { - return details::var_name(); - }(std::make_index_sequence()); - struct RetVal { - std::array pairs{}; - std::size_t total_string_length = 0; - std::size_t valid_count = 0; - } ret; - std::size_t index = 0; - constexpr auto enum_in_array_len = enum_in_array_name().size(); - while (index < Array.size()) { - if (str.front() == '(') { - str.remove_prefix(sizeof("(") - 1 + length_of_enum_in_template_array_casting + sizeof(")0") - - 1); // there is atleast 1 base 10 digit - //if(!str.empty()) - // std::cout << "after str \"" << str << '"' << '\n'; - - if (const auto commapos = str.find(','); commapos != str.npos) - str.remove_prefix(commapos + 2); - - //std::cout << "strsize \"" << str.size() << '"' << '\n'; - } - else { - if constexpr (enum_in_array_len != 0) - str.remove_prefix(enum_in_array_len + sizeof("::") - 1); - if constexpr (details::prefix_length_or_zero != 0) { - str.remove_prefix(details::prefix_length_or_zero); - } - const auto commapos = str.find(','); - - const auto name = str.substr(0, commapos); - - ret.pairs[ret.valid_count] = Pair{Array[index], name}; - ret.total_string_length += name.size() + ShouldNullTerminate; - - if (commapos != str.npos) - str.remove_prefix(commapos + 2); - ++ret.valid_count; - } - ++index; - } - return ret; - }(); - - constexpr auto strings = [elements]() { - std::array strings; - for (std::size_t _i = 0, index = 0; _i < elements.valid_count; ++_i) { - const auto& [_, s] = elements.pairs[_i]; - for (std::size_t i = 0; i < s.size(); ++i) - strings[index++] = s[i]; - - if constexpr (ShouldNullTerminate) - strings[index++] = '\0'; - } - return strings; - }(); - - std::array ret; - constexpr const auto* str = static_storage_for.data(); - for (std::size_t i = 0, string_index = 0; i < elements.valid_count; ++i) { - const auto& [e, s] = elements.pairs[i]; - auto& [re, rs] = ret[i]; - re = e; - - rs = {str + string_index, str + string_index + s.size()}; - string_index += s.size() + ShouldNullTerminate; - } - return ret; - } - -} // namespace details - -} // namespace enchantum -#elif defined(_MSC_VER) - - -#include -#include -#include -#include -#include - -namespace enchantum { - -namespace details { - -#define SZC(x) (sizeof(x) - 1) - template - constexpr auto type_name_func_size() noexcept - { - // (sizeof("auto __cdecl enchantum::details::type_name_func<") - 1) - return SZC(__FUNCSIG__) - SZC("auto __cdecl enchantum::details::type_name_func_size(void) noexcept"); - } - - template - constexpr auto enum_in_array_name() noexcept - { - string_view s = __FUNCSIG__ + sizeof("auto __cdecl enchantum::details::enum_in_array_name<") - 1; - s.remove_suffix(sizeof(">(void) noexcept") - 1); - - if constexpr (ScopedEnum) { - if (s.front() == '(') { - s.remove_prefix(sizeof("(enum ") - 1); - s.remove_suffix(sizeof(")0x0") - 1); - return s; - } - return s.substr(0, s.rfind("::")); - } - else { - if (s.front() == '(') { - s.remove_prefix(sizeof("(enum ") - 1); - s.remove_suffix(sizeof(")0x0") - 1); - } - if (const auto pos = s.rfind("::"); pos != s.npos) - return s.substr(0, pos); - return string_view(); - } - } - - template - constexpr auto var_name() noexcept - { - //auto __cdecl f{enum `anonymous-namespace'::UnscopedAnon - - using T = typename decltype(Array)::value_type; -#define SZC(x) (sizeof(x) - 1) - std::size_t funcsig_off = SZC("auto __cdecl enchantum::details::var_name(); - funcsig_off += type_name_len + SZC(","); - constexpr auto Size = Array.size(); - // clang-format off - funcsig_off += Size < 10 ? 1 - : Size < 100 ? 2 - : Size < 1000 ? 3 - : Size < 10000 ? 4 - : Size < 100000 ? 5 - : Size < 1000000 ? 6 - : Size < 10000000 ? 7 - : Size < 100000000 ? 8 - : Size < 1000000000 ? 9 - : 10; - // clang-format on - funcsig_off += SZC(">{enum ") + type_name_len; - return string_view(__FUNCSIG__ + funcsig_off, SZC(__FUNCSIG__) - funcsig_off - (sizeof("}>(void) noexcept") - 1)); - } -#undef SZC - - template - inline constexpr auto static_storage_for = Copy; - - template - constexpr auto get_elements() - { - constexpr auto type_name_len = details::type_name_func_size(); - - auto str = var_name(); - struct RetVal { - std::array pairs{}; - std::size_t total_string_length = 0; - std::size_t valid_count = 0; - } ret; - std::size_t index = 0; - constexpr auto enum_in_array_len = details::enum_in_array_name().size(); - while (index < Array.size()) { - if (str.front() == '(') { - str.remove_prefix(sizeof("(enum ") - 1 + type_name_len + sizeof(")0x0") - 1); // there is atleast 1 base 16 hex digit - - if (const auto commapos = str.find(','); commapos != str.npos) - str.remove_prefix(commapos + 1); - } - else { - if constexpr (enum_in_array_len != 0) - str.remove_prefix(enum_in_array_len + sizeof("::") - 1); - - if constexpr (details::prefix_length_or_zero != 0) - str.remove_prefix(details::prefix_length_or_zero); - - const auto commapos = str.find(','); - - const auto name = str.substr(0, commapos); - - ret.pairs[ret.valid_count] = Pair{Array[index], name}; - ret.total_string_length += name.size() + ShouldNullTerminate; - - if (commapos != str.npos) - str.remove_prefix(commapos + 1); - ++ret.valid_count; - } - ++index; - } - return ret; - } - - template - constexpr auto reflect() noexcept - { - constexpr auto Min = enum_traits::min; - constexpr auto Max = enum_traits::max; - - constexpr auto elements = details::get_elements(), ShouldNullTerminate>(); - - constexpr auto strings = [elements]() { - std::array strings; - for (std::size_t _i = 0, index = 0; _i < elements.valid_count; ++_i) { - const auto& [_, s] = elements.pairs[_i]; - for (std::size_t i = 0; i < s.size(); ++i) - strings[index++] = s[i]; - - if constexpr (ShouldNullTerminate) - strings[index++] = '\0'; - } - return strings; - }(); - - std::array ret; - constexpr const auto* str = static_storage_for.data(); - for (std::size_t i = 0, string_index = 0; i < elements.valid_count; ++i) { - const auto& [e, s] = elements.pairs[i]; - auto& [re, rs] = ret[i]; - re = e; - - rs = {str + string_index, str + string_index + s.size()}; - string_index += s.size() + ShouldNullTerminate; - } - - return ret; - } -} // namespace details - -} // namespace enchantum - -#endif - -#include -#include - -namespace enchantum { - -#ifdef __cpp_lib_to_underlying -using ::std::to_underlying; -#else -template -[[nodiscard]] constexpr auto to_underlying(const E e) noexcept -{ - return static_cast>(e); -} -#endif - -template, bool ShouldNullTerminate = true> -inline constexpr auto entries = details::reflect, Pair, ShouldNullTerminate>(); - -template -inline constexpr auto values = []() { - constexpr auto& enums = entries; - std::array ret; - for (std::size_t i = 0; i < ret.size(); ++i) - ret[i] = enums[i].first; - return ret; -}(); - -template -inline constexpr auto names = []() { - constexpr auto& enums = entries, NullTerminated>; - std::array ret; - for (std::size_t i = 0; i < ret.size(); ++i) - ret[i] = enums[i].second; - return ret; -}(); - -template -inline constexpr auto min = entries.front().first; - -template -inline constexpr auto max = entries.back().first; - -template -inline constexpr std::size_t count = entries.size(); - -} // namespace enchantum -#include -#include - -namespace enchantum { - -template -inline constexpr bool has_zero_flag = false; - -template -inline constexpr bool has_zero_flag = []() { - for (const auto v : values) - if (static_cast>(v) == 0) - return true; - return false; -}(); - -template -inline constexpr bool is_contiguous = false; - -template -inline constexpr bool is_contiguous = []() { - using T = std::underlying_type_t; - if constexpr (std::is_same_v) { - return true; - } - else { - constexpr auto& enums = entries; - for (std::size_t i = 0; i < enums.size() - 1; ++i) - if (T(enums[i].first) + 1 != T(enums[i + 1].first)) - return false; - return true; - } -}(); - -template -concept ContiguousEnum = Enum && is_contiguous; - -template -inline constexpr bool is_contiguous_bitflag = false; - -template -inline constexpr bool is_contiguous_bitflag = []() { - constexpr auto& enums = entries; - using T = std::underlying_type_t; - for (auto i = std::size_t{has_zero_flag}; i < enums.size() - 1; ++i) - if (T(enums[i].first) << 1 != T(enums[i + 1].first)) - return false; - return true; -}(); - -template -concept ContiguousBitFlagEnum = BitFlagEnum && is_contiguous_bitflag; - -template -[[nodiscard]] constexpr bool contains(const E value) noexcept -{ - for (const auto v : values) - if (v == value) - return true; - return false; -} - -template -[[nodiscard]] constexpr bool contains(const std::underlying_type_t value) noexcept -{ - return enchantum::contains(static_cast(value)); -} - -template -[[nodiscard]] constexpr bool contains(const string_view name) noexcept -{ - for (const auto& s : names) - if (s == name) - return true; - return false; -} - -template BinaryPredicate> -[[nodiscard]] constexpr bool contains(const string_view name, const BinaryPredicate binary_predicate) noexcept -{ - for (const auto& s : names) - if (binary_predicate(name, s)) - return true; - return false; -} - -template -[[nodiscard]] constexpr bool contains(const E value) noexcept -{ - using T = std::underlying_type_t; - return T(value) <= T(max) && T(value) >= T(min); -} - -namespace details { - template - struct index_to_enum_functor { - [[nodiscard]] constexpr optional operator()(const std::size_t index) const noexcept - { - optional ret; - if (index < values.size()) - ret.emplace(values[index]); - return ret; - } - }; - - struct enum_to_index_functor { - template - [[nodiscard]] constexpr optional operator()(const E e) const noexcept - { - if constexpr (ContiguousEnum) { - using T = std::underlying_type_t; - if (enchantum::contains(e)) - return optional(std::size_t(T(e) - T(min))); - } - else { - for (std::size_t i = 0; i < values.size(); ++i) - if (values[i] == e) - return optional(i); - } - return optional(); - } - }; - - template - struct cast_functor { - [[nodiscard]] constexpr optional operator()(const std::underlying_type_t value) const noexcept - { - optional a; // rvo not that it really matters - if (!enchantum::contains(value)) - return a; - a.emplace(static_cast(value)); - return a; - } - - [[nodiscard]] constexpr optional operator()(const string_view name) const noexcept - { - optional a; // rvo not that it really matters - for (const auto& [e, s] : entries) { - if (s == name) { - a.emplace(e); - return a; - } - } - return a; // nullopt - } - - template BinaryPred> - [[nodiscard]] constexpr optional operator()(const string_view name, const BinaryPred binary_predicate) const noexcept - { - optional a; // rvo not that it really matters - for (const auto& [e, s] : entries) { - if (binary_predicate(name, s)) { - a.emplace(e); - return a; - } - } - return a; - } - }; - -} // namespace details - -template -inline constexpr details::index_to_enum_functor index_to_enum{}; - -inline constexpr details::enum_to_index_functor enum_to_index{}; - -template -inline constexpr details::cast_functor cast{}; - -namespace details { - struct to_string_functor { - template - [[nodiscard]] constexpr string_view operator()(const E value) const noexcept - { - if (const auto i = enchantum::enum_to_index(value)) - return names[*i]; - return string_view(); - } - }; - -} // namespace details -inline constexpr details::to_string_functor to_string{}; - -} // namespace enchantum -#include -#include - -namespace enchantum { - -#if 0 -namespace details { - - template - constexpr auto cartesian_product() - { - constexpr auto size = []() { - std::size_t x = range; - std::size_t n = sets; - while (--n != 0) - x *= range; - return x; - }(); - - std::array, size> products{}; - std::array counter{}; - - for (auto& product : products) { - product = counter; - - ++counter.back(); - for (std::size_t i = counter.size() - 1; i != 0; i--) { - if (counter[i] != range) - break; - - counter[i] = 0; - ++counter[i - 1]; - } - } - return products; - } - -} // namespace details -#endif - -#if 0 -template Func> -constexpr auto visit(Func func, E e) -noexcept(std::is_nothrow_invocable_v) -{ - using Ret = decltype(func(e)); - - - return [&](std::index_sequence) { - if ((values[Idx] == enums)) - (func(std::integral_constant[Idx]> {}), ...); - }(std::make_index_sequence>()); -} -template Func> -constexpr auto visit(Func func, Enums... enums) noexcept(std::is_nothrow_invocable_v) -{ - using Ret = decltype(func(enums...)); - return [&](std::index_sequence) { - if ((values[Idx] == enums) && ...) - (func(std::integral_constant[Idx]> {}), ...); - }(std::make_index_sequence>()...); -} -#endif -namespace details { - - template - constexpr auto for_each(Func& f, std::index_sequence) - { - (void)(f(std::integral_constant[I]> {}), ...); - } - -} // namespace details - -template -constexpr void for_each(Func f) // intentional not const -{ - details::for_each(f, std::make_index_sequence>{}); -} -} // namespace enchantum - -#include -#include - -namespace enchantum { - -template -class array : public std::array> { -private: - using base = std::array>; -public: - using index_type = E; - using typename base::const_iterator; - using typename base::const_pointer; - using typename base::const_reference; - using typename base::const_reverse_iterator; - using typename base::difference_type; - using typename base::iterator; - using typename base::pointer; - using typename base::reference; - using typename base::reverse_iterator; - using typename base::size_type; - using typename base::value_type; - - using base::at; - using base::operator[]; - - [[nodiscard]] constexpr reference at(const E index) - { - if (const auto i = enchantum::enum_to_index(index)) - return operator[](*i); - ENCHANTUM_THROW(std::out_of_range("enchantum::array::at index out of range"), index); - } - - [[nodiscard]] constexpr const_reference at(const E index) const - { - if (const auto i = enchantum::enum_to_index(index)) - return operator[](*i); - ENCHANTUM_THROW(std::out_of_range("enchantum::array::at: index out of range"), index); - } - - [[nodiscard]] constexpr reference operator[](const E index) noexcept - { - return operator[](*enchantum::enum_to_index(index)); - } - - [[nodiscard]] constexpr const_reference operator[](const E index) const noexcept - { - return operator[](*enchantum::enum_to_index(index)); - } -}; - -} // namespace enchantum - -#ifndef ENCHANTUM_ALIAS_STRING - #include -#endif - -namespace enchantum { -#ifdef ENCHANTUM_ALIAS_STRING -ENCHANTUM_ALIAS_STRING; -#else -using ::std::string; -#endif -} // namespace enchantum - -namespace enchantum { - -template -inline constexpr E values_ors = [] { - E ret{}; - for (const auto val : values) - ret |= val; - return ret; -}(); - -template -[[nodiscard]] constexpr bool contains_bitflag(const std::underlying_type_t value) noexcept -{ - if (value == 0) - return has_zero_flag; - using T = std::underlying_type_t; - T valid_bits = 0; - - for (auto i = std::size_t{has_zero_flag}; i < count; ++i) { - const auto v = static_cast(values[i]); - if ((value & v) == v) - valid_bits |= v; - } - return valid_bits == value; -} - -template -[[nodiscard]] constexpr bool contains_bitflag(const E value) noexcept -{ - return enchantum::contains_bitflag(static_cast>(value)); -} - -template BinaryPred> -[[nodiscard]] constexpr bool contains_bitflag(const string_view s, const char sep, const BinaryPred binary_pred) noexcept -{ - std::size_t pos = 0; - for (std::size_t i = s.find(sep); i != s.npos; i = s.find(sep, pos)) { - if (!enchantum::contains(s.substr(pos, i - pos)), binary_pred) - return false; - pos = i + 1; - } - return enchantum::contains(s.substr(pos), binary_pred); -} - -template -[[nodiscard]] constexpr bool contains_bitflag(const string_view s, const char sep = '|') noexcept -{ - std::size_t pos = 0; - for (std::size_t i = s.find(sep); i != s.npos; i = s.find(sep, pos)) { - if (!enchantum::contains(s.substr(pos, i - pos))) - return false; - pos = i + 1; - } - return enchantum::contains(s.substr(pos)); -} - -template -[[nodiscard]] constexpr String to_string_bitflag(const E value, const char sep = '|') -{ - using T = std::underlying_type_t; - if constexpr (has_zero_flag) - if (static_cast(value) == 0) - return String(names[0]); - - String name; - T check_value = 0; - for (auto i = std::size_t{has_zero_flag}; i < count; ++i) { - const auto& [v, s] = entries[i]; - if (v == (value & v)) { - if (!name.empty()) - name.append(1, sep); // append separator if not the first value - name.append(s.data(), s.size()); // not using operator += since this may not be std::string_view always - check_value |= static_cast(v); - } - } - if (check_value == static_cast(value)) - return name; - return String{}; -} - -template BinaryPred> -[[nodiscard]] constexpr optional cast_bitflag(const string_view s, const char sep, const BinaryPred binary_pred) noexcept -{ - using T = std::underlying_type_t; - T check_value{}; - std::size_t pos = 0; - for (std::size_t i = s.find(sep); i != s.npos; i = s.find(sep, pos)) { - if (const auto v = enchantum::cast(s.substr(pos, i - pos),binary_pred)) - check_value |= static_cast(*v); - else - return optional(); - pos = i + 1; - } - - if (const auto v = enchantum::cast(s.substr(pos), binary_pred)) - return optional(static_cast(check_value | static_cast(*v))); - return optional(); -} - -template -[[nodiscard]] constexpr optional cast_bitflag(const string_view s, const char sep = '|') noexcept -{ - return enchantum::cast_bitflag(s, sep, [](const auto& a, const auto& b) { return a == b; }); -} - -template -[[nodiscard]] constexpr optional cast_bitflag(const E value) noexcept -{ - using T = std::underlying_type_t; - const auto raw_value = static_cast(value); - - if constexpr (has_zero_flag) - if (raw_value == 0) - return optional(E{}); - - T valid_bits{0}; - for (auto i = std::size_t{has_zero_flag}; i < count; ++i) { - auto v = static_cast(values[i]); - if ((raw_value & v) == v) - valid_bits |= v; - } - return valid_bits == raw_value ? optional(value) : optional(); -} - -} // namespace enchantum - -#include -/* -Note this header is an extremely easy way to cause ODR issues. - -class Flags { F1 = 1 << 0,F2 = 1<< 1}; -// **note I did not define any operators** - -enchantum::contains(Flags::F1); // considered a classical `Enum` concept - -using namespace enchantum::bitwise_operators; - -enchantum::contains(Flags::F1); // considered `BitFlagEnum` concept woops! ODR! - -*/ - -namespace enchantum::bitwise_operators { -template -[[nodiscard]] constexpr E operator~(E e) noexcept -{ - return static_cast(~static_cast>(e)); -} - -template -[[nodiscard]] constexpr E operator|(E a, E b) noexcept -{ - using T = std::underlying_type_t; - return static_cast(static_cast(a) | static_cast(b)); -} - -template -[[nodiscard]] constexpr E operator&(E a, E b) noexcept -{ - using T = std::underlying_type_t; - return static_cast(static_cast(a) & static_cast(b)); -} - -template -[[nodiscard]] constexpr E operator^(E a, E b) noexcept -{ - using T = std::underlying_type_t; - return static_cast(static_cast(a) ^ static_cast(b)); -} - -template -constexpr E& operator|=(E& a, E b) noexcept -{ - using T = std::underlying_type_t; - return a = static_cast(static_cast(a) | static_cast(b)); -} - -template -constexpr E& operator&=(E& a, E b) noexcept -{ - using T = std::underlying_type_t; - return a = static_cast(static_cast(a) & static_cast(b)); -} - -template -constexpr E& operator^=(E& a, E b) noexcept -{ - using T = std::underlying_type_t; - return a = static_cast(static_cast(a) ^ static_cast(b)); -} - -} // namespace enchantum::bitwise_operators - -#define ENCHANTUM_DEFINE_BITWISE_FOR(Enum) \ - [[nodiscard]] constexpr Enum operator&(Enum a, Enum b) noexcept \ - { \ - using T = std::underlying_type_t; \ - return static_cast(static_cast(a) & static_cast(b)); \ - } \ - [[nodiscard]] constexpr Enum operator|(Enum a, Enum b) noexcept \ - { \ - using T = std::underlying_type_t; \ - return static_cast(static_cast(a) | static_cast(b)); \ - } \ - [[nodiscard]] constexpr Enum operator^(Enum a, Enum b) noexcept \ - { \ - using T = std::underlying_type_t; \ - return static_cast(static_cast(a) ^ static_cast(b)); \ - } \ - constexpr Enum& operator&=(Enum& a, Enum b) noexcept { return a = a & b; } \ - constexpr Enum& operator|=(Enum& a, Enum b) noexcept { return a = a | b; } \ - constexpr Enum& operator^=(Enum& a, Enum b) noexcept { return a = a ^ b; } \ - [[nodiscard]] constexpr Enum operator~(Enum a) noexcept \ - { \ - return static_cast(~static_cast>(a)); \ - } - -#include -#include -#include - -namespace enchantum::istream_operators { -template - requires std::assignable_from -std::basic_istream& operator>>(std::basic_istream& is, E& value) -{ - std::basic_string s; - is >> s; - if (!is) - return is; - - if (const auto v = enchantum::cast(s)) - value = *v; - else - is.setstate(std::ios_base::failbit); - return is; -} - -} // namespace enchantum::istream_operators - -#include -#include - -namespace enchantum::ostream_operators { -template -std::basic_ostream& operator<<(std::basic_ostream& os, const E value) -{ - return os << enchantum::to_string(value); -} -} // namespace enchantum::ostream_operators - -namespace enchantum::iostream_operators { -using ::enchantum::istream_operators::operator>>; -using ::enchantum::ostream_operators::operator<<; -} // namespace enchantum::iostream_operators - -#include - -namespace enchantum { -namespace details { - template - struct next_value_functor { - template - [[nodiscard]] constexpr optional operator()(const E value, const std::ptrdiff_t n = 1) const noexcept - { - if (!enchantum::contains(value)) - return optional{}; - - const auto index = static_cast(*enchantum::enum_to_index(value)) + (n * N); - if (index >= 0 && index < static_cast(count)) - return optional{values[static_cast(index)]}; - return optional{}; - } - }; - - template - struct next_value_circular_functor { - template - [[nodiscard]] constexpr E operator()(const E value, const std::ptrdiff_t n = 1) const noexcept - { - ENCHANTUM_ASSERT(enchantum::contains(value), "next/prev_value_circular requires 'value' to be a valid enum member", value); - const auto i = static_cast(*enchantum::enum_to_index(value)); - constexpr auto count = static_cast(enchantum::count); - return values[static_cast(((i + (n * N)) % count + count) % count)]; // handles wrap around and negative n - } - }; -} // namespace details - -inline constexpr details::next_value_functor<1> next_value{}; -inline constexpr details::next_value_functor<-1> prev_value{}; -inline constexpr details::next_value_circular_functor<1> next_value_circular{}; -inline constexpr details::next_value_circular_functor<-1> prev_value_circular{}; - -} // namespace enchantum - -#if __has_include() - - -#include -#include - -template -struct fmt::formatter { - template - static constexpr auto parse(ParseContext& ctx) - { - return ctx.begin(); - } - - template - static auto format(const E e, FmtContext& ctx) - { - if constexpr (enchantum::BitFlagEnum) - return fmt::format_to(ctx.out(), "{}", enchantum::to_string_bitflag(e)); - else - return fmt::format_to(ctx.out(), "{}", enchantum::to_string(e)); - } -}; -#elif __has_include() - - -#include -#include - -template -struct std::formatter { - template - static constexpr auto parse(ParseContext& ctx) - { - return ctx.begin(); - } - - template - static auto format(const E e, FmtContext& ctx) - { - if constexpr (enchantum::BitFlagEnum) - return std::format_to(ctx.out(), "{}", enchantum::to_string_bitflag(e)); - else - return std::format_to(ctx.out(), "{}", enchantum::to_string(e)); - } -}; -#endif \ No newline at end of file diff --git a/include/rfl/thirdparty/enchantum/algorithms.hpp b/include/rfl/thirdparty/enchantum/algorithms.hpp new file mode 100644 index 00000000..a770aeb5 --- /dev/null +++ b/include/rfl/thirdparty/enchantum/algorithms.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include "enchantum.hpp" +#include +#include + +namespace enchantum { + +#if 0 +namespace details { + + template + constexpr auto cartesian_product() + { + constexpr auto size = []() { + std::size_t x = range; + std::size_t n = sets; + while (--n != 0) + x *= range; + return x; + }(); + + std::array, size> products{}; + std::array counter{}; + + for (auto& product : products) { + product = counter; + + ++counter.back(); + for (std::size_t i = counter.size() - 1; i != 0; i--) { + if (counter[i] != range) + break; + + counter[i] = 0; + ++counter[i - 1]; + } + } + return products; + } + +} // namespace details +#endif + +#if 0 +template Func> +constexpr auto visit(Func func, E e) +noexcept(std::is_nothrow_invocable_v) +{ + using Ret = decltype(func(e)); + + + return [&](std::index_sequence) { + if ((values[Idx] == enums)) + (func(std::integral_constant[Idx]> {}), ...); + }(std::make_index_sequence>()); +} +template Func> +constexpr auto visit(Func func, Enums... enums) noexcept(std::is_nothrow_invocable_v) +{ + using Ret = decltype(func(enums...)); + return [&](std::index_sequence) { + if ((values[Idx] == enums) && ...) + (func(std::integral_constant[Idx]> {}), ...); + }(std::make_index_sequence>()...); +} +#endif +namespace details { + + template + constexpr auto for_each(Func& f, std::index_sequence) + { + (void)(f(std::integral_constant[I]> {}), ...); + } + +} // namespace details + +template +constexpr void for_each(Func f) // intentional not const +{ + details::for_each(f, std::make_index_sequence>{}); +} +} // namespace enchantum \ No newline at end of file diff --git a/include/rfl/thirdparty/enchantum/all.hpp b/include/rfl/thirdparty/enchantum/all.hpp new file mode 100644 index 00000000..c6038a82 --- /dev/null +++ b/include/rfl/thirdparty/enchantum/all.hpp @@ -0,0 +1,20 @@ +#pragma once +#include "details/optional.hpp" +#include "details/string_view.hpp" +#include "details/generate_arrays.hpp" + +#include "algorithms.hpp" +#include "array.hpp" +#include "bitflags.hpp" +#include "bitwise_operators.hpp" +#include "enchantum.hpp" +#include "entries.hpp" +#include "iostream.hpp" +#include "next_value.hpp" +#include "bitset.hpp" + +#if __has_include() + #include "fmt_format.hpp" +#elif __has_include() + #include "std_format.hpp" +#endif \ No newline at end of file diff --git a/include/rfl/thirdparty/enchantum/array.hpp b/include/rfl/thirdparty/enchantum/array.hpp new file mode 100644 index 00000000..7e990d9b --- /dev/null +++ b/include/rfl/thirdparty/enchantum/array.hpp @@ -0,0 +1,54 @@ +#pragma once +#include "enchantum.hpp" +#include +#include + +namespace enchantum { + +template +class array : public std::array> { +private: + using base = std::array>; +public: + using index_type = E; + using typename base::const_iterator; + using typename base::const_pointer; + using typename base::const_reference; + using typename base::const_reverse_iterator; + using typename base::difference_type; + using typename base::iterator; + using typename base::pointer; + using typename base::reference; + using typename base::reverse_iterator; + using typename base::size_type; + using typename base::value_type; + + using base::at; + using base::operator[]; + + [[nodiscard]] constexpr reference at(const E index) + { + if (const auto i = enchantum::enum_to_index(index)) + return operator[](*i); + ENCHANTUM_THROW(std::out_of_range("enchantum::array::at index out of range"), index); + } + + [[nodiscard]] constexpr const_reference at(const E index) const + { + if (const auto i = enchantum::enum_to_index(index)) + return operator[](*i); + ENCHANTUM_THROW(std::out_of_range("enchantum::array::at: index out of range"), index); + } + + [[nodiscard]] constexpr reference operator[](const E index) noexcept + { + return operator[](*enchantum::enum_to_index(index)); + } + + [[nodiscard]] constexpr const_reference operator[](const E index) const noexcept + { + return operator[](*enchantum::enum_to_index(index)); + } +}; + +} // namespace enchantum diff --git a/include/rfl/thirdparty/enchantum/bitflags.hpp b/include/rfl/thirdparty/enchantum/bitflags.hpp new file mode 100644 index 00000000..d7edc341 --- /dev/null +++ b/include/rfl/thirdparty/enchantum/bitflags.hpp @@ -0,0 +1,149 @@ +#pragma once +#include "common.hpp" +#include "details/string_view.hpp" +#include "enchantum.hpp" + +#ifndef ENCHANTUM_ALIAS_STRING + #include +#endif + +namespace enchantum { +#ifdef ENCHANTUM_ALIAS_STRING +ENCHANTUM_ALIAS_STRING; +#else +using ::std::string; +#endif +} // namespace enchantum + + +namespace enchantum { + + +template +inline constexpr E value_ors = [] { + using T = std::underlying_type_t; + T ret{}; + for (const auto val : values) + ret |= static_cast(val); + return static_cast(ret); +}(); + + +template +[[nodiscard]] constexpr bool contains_bitflag(const std::underlying_type_t value) noexcept +{ + if (value == 0) + return has_zero_flag; + using T = std::underlying_type_t; + T valid_bits = 0; + + for (auto i = std::size_t{has_zero_flag}; i < count; ++i) { + const auto v = static_cast(values[i]); + if ((value & v) == v) + valid_bits |= v; + } + return valid_bits == value; +} + +template +[[nodiscard]] constexpr bool contains_bitflag(const E value) noexcept +{ + return enchantum::contains_bitflag(static_cast>(value)); +} + +template BinaryPred> +[[nodiscard]] constexpr bool contains_bitflag(const string_view s, const char sep, const BinaryPred binary_pred) noexcept +{ + std::size_t pos = 0; + for (std::size_t i = s.find(sep); i != s.npos; i = s.find(sep, pos)) { + if (!enchantum::contains(s.substr(pos, i - pos)), binary_pred) + return false; + pos = i + 1; + } + return enchantum::contains(s.substr(pos), binary_pred); +} + + +template +[[nodiscard]] constexpr bool contains_bitflag(const string_view s, const char sep = '|') noexcept +{ + std::size_t pos = 0; + for (std::size_t i = s.find(sep); i != s.npos; i = s.find(sep, pos)) { + if (!enchantum::contains(s.substr(pos, i - pos))) + return false; + pos = i + 1; + } + return enchantum::contains(s.substr(pos)); +} + + +template +[[nodiscard]] constexpr String to_string_bitflag(const E value, const char sep = '|') +{ + using T = std::underlying_type_t; + if constexpr (has_zero_flag) + if (static_cast(value) == 0) + return String(names[0]); + + String name; + T check_value = 0; + for (auto i = std::size_t{has_zero_flag}; i < count; ++i) { + const auto& [v, s] = entries[i]; + if (v == (value & v)) { + if (!name.empty()) + name.append(1, sep); // append separator if not the first value + name.append(s.data(), s.size()); // not using operator += since this may not be std::string_view always + check_value |= static_cast(v); + } + } + if (check_value == static_cast(value)) + return name; + return String(); +} + +template BinaryPred> +[[nodiscard]] constexpr optional cast_bitflag(const string_view s, const char sep, const BinaryPred binary_pred) noexcept +{ + using T = std::underlying_type_t; + T check_value{}; + std::size_t pos = 0; + for (std::size_t i = s.find(sep); i != s.npos; i = s.find(sep, pos)) { + if (const auto v = enchantum::cast(s.substr(pos, i - pos), binary_pred)) + check_value |= static_cast(*v); + else + return optional(); + pos = i + 1; + } + + if (const auto v = enchantum::cast(s.substr(pos), binary_pred)) + return optional(static_cast(check_value | static_cast(*v))); + return optional(); +} + +template +[[nodiscard]] constexpr optional cast_bitflag(const string_view s, const char sep = '|') noexcept +{ + return enchantum::cast_bitflag(s, sep, [](const auto& a, const auto& b) { return a == b; }); +} + +template +[[nodiscard]] constexpr optional cast_bitflag(const E value) noexcept +{ + using T = std::underlying_type_t; + const auto raw_value = static_cast(value); + + if constexpr (has_zero_flag) + if (raw_value == 0) + return optional(E{}); + + T valid_bits{0}; + for (auto i = std::size_t{has_zero_flag}; i < count; ++i) { + const auto v = static_cast(values[i]); + if ((raw_value & v) == v) + valid_bits |= v; + } + return valid_bits == raw_value ? optional(value) : optional(); +} + + +} // namespace enchantum \ No newline at end of file diff --git a/include/rfl/thirdparty/enchantum/bitset.hpp b/include/rfl/thirdparty/enchantum/bitset.hpp new file mode 100644 index 00000000..f0820ec6 --- /dev/null +++ b/include/rfl/thirdparty/enchantum/bitset.hpp @@ -0,0 +1,109 @@ +#pragma once +#include "enchantum.hpp" +#ifndef ENCHANTUM_ALIAS_BITSET + #include +#endif +#include "bitflags.hpp" +#include + +namespace enchantum { + +namespace details { +#ifndef ENCHANTUM_ALIAS_BITSET + using ::std::bitset; +#else + ENCHANTUM_ALIAS_BITSET; +#endif +} // namespace details + +template +class bitset : public details::bitset> { +private: + using base = details::bitset>; +public: + using typename base::reference; + + using base::operator[]; + using base::flip; + using base::reset; + using base::set; + using base::test; + + using base::base; + using base::operator=; + + [[nodiscard]] constexpr string to_string(const char sep = '|') const + { + string name; + for (std::size_t i = 0; i < enchantum::count; ++i) { + if (test(i)) { + const auto s = enchantum::names[i]; + if (!name.empty()) + name += sep; + name.append(s.data(), s.size()); // not using operator += since this may not be std::string_view always + } + } + return name; + } + + [[nodiscard]] constexpr auto to_string(const char zero,const char one) const + { + return base::to_string(zero,one); + } + + constexpr bitset(const std::initializer_list values) noexcept + { + for (auto value : values) { + set(value, true); + } + } + + [[nodiscard]] constexpr reference operator[](const E index) noexcept + { + return operator[](*enchantum::enum_to_index(index)); + } + + [[nodiscard]] constexpr bool operator[](const E index) const noexcept + { + return operator[](*enchantum::enum_to_index(index)); + } + + constexpr bool test(const E pos) + { + + if (const auto i = enchantum::enum_to_index(pos)) + return test(*i); + ENCHANTUM_THROW(std::out_of_range("enchantum::bitset::test(E pos,bool value) out of range exception"), pos); + } + + constexpr bitset& set(const E pos, bool value = true) + { + + if (const auto i = enchantum::enum_to_index(pos)) + return static_cast(set(*i, value)); + ENCHANTUM_THROW(std::out_of_range("enchantum::bitset::set(E pos,bool value) out of range exception"), pos); + } + + + constexpr bitset& reset(const E pos) + { + if (const auto i = enchantum::enum_to_index(pos)) + return static_cast(reset(*i)); + ENCHANTUM_THROW(std::out_of_range("enchantum::bitset::reset(E pos) out of range exception"), pos); + } + + constexpr bitset& flip(const E pos) + { + if (const auto i = enchantum::enum_to_index(pos)) + return static_cast(flip(*i)); + ENCHANTUM_THROW(std::out_of_range("enchantum::bitset::flip(E pos) out of range exception"), pos); + } +}; + +} // namespace enchantum + + +template +struct std::hash> : std::hash>> { + using std::hash>>::operator(); +}; diff --git a/include/rfl/thirdparty/enchantum/bitwise_operators.hpp b/include/rfl/thirdparty/enchantum/bitwise_operators.hpp new file mode 100644 index 00000000..5e52623c --- /dev/null +++ b/include/rfl/thirdparty/enchantum/bitwise_operators.hpp @@ -0,0 +1,93 @@ +#pragma once + +#include "common.hpp" +#include +/* +Note this header is an extremely easy way to cause ODR issues. + +class Flags { F1 = 1 << 0,F2 = 1<< 1}; +// **note I did not define any operators** + +enchantum::contains(Flags::F1); // considered a classical `Enum` concept + +using namespace enchantum::bitwise_operators; + +enchantum::contains(Flags::F1); // considered `BitFlagEnum` concept woops! ODR! + +*/ + +namespace enchantum::bitwise_operators { +template +[[nodiscard]] constexpr E operator~(E e) noexcept +{ + return static_cast(~static_cast>(e)); +} + +template +[[nodiscard]] constexpr E operator|(E a, E b) noexcept +{ + using T = std::underlying_type_t; + return static_cast(static_cast(a) | static_cast(b)); +} + +template +[[nodiscard]] constexpr E operator&(E a, E b) noexcept +{ + using T = std::underlying_type_t; + return static_cast(static_cast(a) & static_cast(b)); +} + +template +[[nodiscard]] constexpr E operator^(E a, E b) noexcept +{ + using T = std::underlying_type_t; + return static_cast(static_cast(a) ^ static_cast(b)); +} + +template +constexpr E& operator|=(E& a, E b) noexcept +{ + using T = std::underlying_type_t; + return a = static_cast(static_cast(a) | static_cast(b)); +} + +template +constexpr E& operator&=(E& a, E b) noexcept +{ + using T = std::underlying_type_t; + return a = static_cast(static_cast(a) & static_cast(b)); +} + +template +constexpr E& operator^=(E& a, E b) noexcept +{ + using T = std::underlying_type_t; + return a = static_cast(static_cast(a) ^ static_cast(b)); +} + +} // namespace enchantum::bitwise_operators + +#define ENCHANTUM_DEFINE_BITWISE_FOR(Enum) \ + [[nodiscard]] constexpr Enum operator&(Enum a, Enum b) noexcept \ + { \ + using T = std::underlying_type_t; \ + return static_cast(static_cast(a) & static_cast(b)); \ + } \ + [[nodiscard]] constexpr Enum operator|(Enum a, Enum b) noexcept \ + { \ + using T = std::underlying_type_t; \ + return static_cast(static_cast(a) | static_cast(b)); \ + } \ + [[nodiscard]] constexpr Enum operator^(Enum a, Enum b) noexcept \ + { \ + using T = std::underlying_type_t; \ + return static_cast(static_cast(a) ^ static_cast(b)); \ + } \ + constexpr Enum& operator&=(Enum& a, Enum b) noexcept { return a = a & b; } \ + constexpr Enum& operator|=(Enum& a, Enum b) noexcept { return a = a | b; } \ + constexpr Enum& operator^=(Enum& a, Enum b) noexcept { return a = a ^ b; } \ + [[nodiscard]] constexpr Enum operator~(Enum a) noexcept \ + { \ + return static_cast(~static_cast>(a)); \ + } + diff --git a/include/rfl/thirdparty/enchantum/common.hpp b/include/rfl/thirdparty/enchantum/common.hpp new file mode 100644 index 00000000..4f9df168 --- /dev/null +++ b/include/rfl/thirdparty/enchantum/common.hpp @@ -0,0 +1,98 @@ +#pragma once +#include +#include +#include +#include + +#ifndef ENCHANTUM_ASSERT + #include +// clang-format off + #define ENCHANTUM_ASSERT(cond, msg, ...) assert(cond && msg) +// clang-format on +#endif + +#ifndef ENCHANTUM_THROW + // additional info such as local variables are here + #define ENCHANTUM_THROW(exception, ...) throw exception +#endif + +#ifndef ENCHANTUM_MAX_RANGE + #define ENCHANTUM_MAX_RANGE 256 +#endif +#ifndef ENCHANTUM_MIN_RANGE + #define ENCHANTUM_MIN_RANGE (-ENCHANTUM_MAX_RANGE) +#endif + +namespace enchantum { + +template +concept Enum = std::is_enum_v; + +template +concept SignedEnum = Enum && std::signed_integral>; + +template +concept UnsignedEnum = Enum && !SignedEnum; + +template +concept ScopedEnum = Enum && (!std::is_convertible_v>); + +template +concept UnscopedEnum = Enum && !ScopedEnum; + +template +concept EnumOfUnderlying = Enum && std::same_as, Underlying>; + +template +inline constexpr bool is_bitflag = requires(E e) { + requires std::same_as || std::same_as; + { ~e } -> std::same_as; + { e | e } -> std::same_as; + { e &= e } -> std::same_as; + { e |= e } -> std::same_as; +}; + +template +concept BitFlagEnum = Enum && is_bitflag; + +template +concept EnumFixedUnderlying = Enum && requires { T{0}; }; + +template +struct enum_traits; + +template +struct enum_traits { +private: + using U = std::underlying_type_t; + using L = std::numeric_limits; +public: + static constexpr std::size_t prefix_length = 0; + + static constexpr auto min = (L::min)() > ENCHANTUM_MIN_RANGE ? (L::min)() : ENCHANTUM_MIN_RANGE; + static constexpr auto max = (L::max)() < ENCHANTUM_MAX_RANGE ? (L::max)() : ENCHANTUM_MAX_RANGE; +}; + +template +struct enum_traits { +private: + using T = std::underlying_type_t; + using L = std::numeric_limits; +public: + static constexpr std::size_t prefix_length = 0; + + static constexpr auto min = []() { + if constexpr (std::is_same_v) + return false; + else + return (ENCHANTUM_MIN_RANGE) < 0 ? 0 : (ENCHANTUM_MIN_RANGE); + }(); + static constexpr auto max = []() { + if constexpr (std::is_same_v) + return true; + else + return (L::max)() < (ENCHANTUM_MAX_RANGE) ? (L::max)() : (ENCHANTUM_MAX_RANGE); + }(); +}; + +} // namespace enchantum \ No newline at end of file diff --git a/include/rfl/thirdparty/enchantum/details/enchantum_clang.hpp b/include/rfl/thirdparty/enchantum/details/enchantum_clang.hpp new file mode 100644 index 00000000..7713c0d0 --- /dev/null +++ b/include/rfl/thirdparty/enchantum/details/enchantum_clang.hpp @@ -0,0 +1,259 @@ +#pragma once +#if __clang_major__ < 20 + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wenum-constexpr-conversion" +#endif + +#include "../common.hpp" +#include "generate_arrays.hpp" +#include "string_view.hpp" +#include +#include +#include +#include +#include + +namespace enchantum { +namespace details { + +#if __clang_major__ >= 20 + + template + inline constexpr bool is_valid_cast = false; + + template + inline constexpr bool is_valid_cast(V)>>> = true; + + template max_range = 1> + constexpr auto valid_cast_range() + { + if constexpr (max_range >= 0) { + if constexpr (max_range <= ENCHANTUM_MAX_RANGE) { + // this tests whether `static_cast`ing max_range is valid + // because C style enums stupidly is like a bit field + // `enum E { a,b,c,d = 3};` is like a bitfield `struct E { int val : 2;}` + // which means giving E.val a larger than 2 bit value is UB so is it for enums + // and gcc and msvc ignore this (for good) + // while clang makes it a subsituation failure which we can check for + // using std::inegral_constant makes sure this is a constant expression situation + // for SFINAE to occur + if constexpr (is_valid_cast) + return valid_cast_range(); + else + return max_range - 1; + } + else { + return max_range - 1; + } + } + else { + if constexpr (max_range >= ENCHANTUM_MIN_RANGE) { + // this tests whether `static_cast`ing max_range is valid + // because C style enums stupidly is like a bit field + // `enum E { a,b,c,d = 3};` is like a bitfield `struct E { int val : 2;}` + // which means giving E.val a larger than 2 bit value is UB so is it for enums + // and gcc and msvc ignore this (for good) + // while clang makes it a subsituation failure which we can check for + // using std::inegral_constant makes sure this is a constant expression situation + // for SFINAE to occur + if constexpr (is_valid_cast) + return valid_cast_range(); + else + return max_range / 2; + } + else { + return max_range / 2; + } + } + } +#else + template max_range = 1> + constexpr auto valid_cast_range() + { + if constexpr (max_range >= 0) + return ENCHANTUM_MAX_RANGE; + else + return ENCHANTUM_MIN_RANGE; + } +#endif + +} // namespace details + +template + requires SignedEnum && (!EnumFixedUnderlying) +struct enum_traits { + static constexpr auto max = details::valid_cast_range(); + static constexpr decltype(max) min = details::valid_cast_range(); +}; + +template + requires UnsignedEnum && (!EnumFixedUnderlying) +struct enum_traits { + static constexpr auto max = details::valid_cast_range(); + static constexpr decltype(max) min = 0; +}; + +namespace details { + + template + constexpr auto type_name_func() noexcept + { + // constexpr auto f() [with _ = Scoped] + //return __PRETTY_FUNCTION__; + constexpr auto funcname = string_view( + __PRETTY_FUNCTION__ + (sizeof("auto enchantum::details::type_name_func() [_ = ") - 1)); + // (sizeof("auto __cdecl enchantum::details::type_name_func<") - 1) + constexpr auto size = funcname.size() - (sizeof("]") - 1); + std::array ret; + auto* const ret_data = ret.data(); + const auto* const funcname_data = funcname.data(); + for (std::size_t i = 0; i < size; ++i) + ret_data[i] = funcname_data[i]; + return ret; + } + + template + inline constexpr auto type_name = type_name_func(); + + + template + constexpr auto enum_in_array_name() noexcept + { + // constexpr auto f() [with auto _ = ( + //constexpr auto f() [Enum = (Scoped)0] + string_view s = __PRETTY_FUNCTION__ + (sizeof("auto enchantum::details::enum_in_array_name() [Enum = ") - 1); + s.remove_suffix(sizeof("]") - 1); + + if constexpr (ScopedEnum) { + if (s[s.size() - 2] == ')') { + s.remove_prefix(sizeof("(") - 1); + s.remove_suffix(sizeof(")0") - 1); + return s; + } + else { + return s.substr(0, s.rfind("::")); + } + } + else { + if (s[s.size() - 2] == ')') { + s.remove_prefix(sizeof("(") - 1); + s.remove_suffix(sizeof(")0") - 1); + } + if (const auto pos = s.rfind("::"); pos != s.npos) + return s.substr(0, pos); + return string_view(); + } + } + + template + constexpr auto var_name() noexcept + { + // "auto enchantum::details::var_name() [Vs = <(A)0, a, b, c, e, d, (A)6>]" +#define SZC(x) (sizeof(x) - 1) + constexpr auto funcsig_off = SZC("auto enchantum::details::var_name() [Vs = <"); + return string_view(__PRETTY_FUNCTION__ + funcsig_off, SZC(__PRETTY_FUNCTION__) - funcsig_off - SZC(">]")); +#undef SZC + } + + + template + inline constexpr auto static_storage_for = Copy; + + template + constexpr auto reflect() noexcept + { + constexpr auto Min = enum_traits::min; + constexpr auto Max = enum_traits::max; + + constexpr auto elements = []() { + constexpr auto Array = details::generate_arrays(); + auto str = [Array](std::index_sequence) { + return details::var_name(); + }(std::make_index_sequence()); + + struct RetVal { + std::array pairs{}; + std::size_t total_string_length = 0; + std::size_t valid_count = 0; + } ret; + + std::size_t index = 0; + constexpr auto enum_in_array_name = details::enum_in_array_name(); + constexpr auto enum_in_array_len = enum_in_array_name.size(); + + // ((anonymous namespace)::A)0 + // (anonymous namespace)::a + + // this is needed to determine whether the above are cast expression if 2 braces are + // next to eachother then it is a cast but only for anonymoused namespaced enums + constexpr std::size_t index_check = !enum_in_array_name.empty() && enum_in_array_name.front() == '(' ? 1 : 0; + while (index < Array.size()) { + if (str[index_check] == '(') { + str.remove_prefix(sizeof("(") - 1 + enum_in_array_len + sizeof(")0") - 1); // there is atleast 1 base 10 digit + //if(!str.empty()) + // std::cout << "after str \"" << str << '"' << '\n'; + if (const auto commapos = str.find(','); commapos != str.npos) + str.remove_prefix(commapos + 2); + //std::cout << "strsize \"" << str.size() << '"' << '\n'; + } + else { + if constexpr (enum_in_array_len != 0) { + str.remove_prefix(enum_in_array_len + (sizeof("::") - 1)); + } + if constexpr (details::prefix_length_or_zero != 0) { + str.remove_prefix(details::prefix_length_or_zero); + } + const auto commapos = str.find(','); + + const auto name = str.substr(0, commapos); + + ret.pairs[ret.valid_count] = Pair{Array[index], name}; + ret.total_string_length += name.size() + ShouldNullTerminate; + + if (commapos != str.npos) + str.remove_prefix(commapos + 2); // skip comma and space + ++ret.valid_count; + } + ++index; + } + return ret; + }(); + + constexpr auto strings = [elements]() { + std::array strings; + for (std::size_t _i = 0, index = 0; _i < elements.valid_count; ++_i) { + const auto& [_, s] = elements.pairs[_i]; + for (std::size_t i = 0; i < s.size(); ++i) + strings[index++] = s[i]; + + if constexpr (ShouldNullTerminate) + strings[index++] = '\0'; + } + return strings; + }(); + + std::array ret; + constexpr const auto* str = static_storage_for.data(); + for (std::size_t i = 0, string_index = 0; i < elements.valid_count; ++i) { + const auto& [e, s] = elements.pairs[i]; + auto& [re, rs] = ret[i]; + re = e; + + rs = {str + string_index, str + string_index + s.size()}; + string_index += s.size() + ShouldNullTerminate; + } + return ret; + } + +} // namespace details + + +//template +//constexpr std::size_t enum_count = details::enum_count; + + +} // namespace enchantum + +#if __clang_major__ < 20 + #pragma clang diagnostic pop +#endif diff --git a/include/rfl/thirdparty/enchantum/details/enchantum_gcc.hpp b/include/rfl/thirdparty/enchantum/details/enchantum_gcc.hpp new file mode 100644 index 00000000..efc0bb0f --- /dev/null +++ b/include/rfl/thirdparty/enchantum/details/enchantum_gcc.hpp @@ -0,0 +1,180 @@ +#include "../common.hpp" +#include "generate_arrays.hpp" +#include +#include +#include +#include +#include + +#include "string_view.hpp" + +namespace enchantum { + +namespace details { + + + template + constexpr auto type_name_func() noexcept + { + // constexpr auto f() [with _ = Scoped] + //return __PRETTY_FUNCTION__; + constexpr auto funcname = string_view( + __PRETTY_FUNCTION__ + (sizeof("constexpr auto enchantum::details::type_name_func() [with _ = ") - 1)); + // (sizeof("auto __cdecl enchantum::details::type_name_func<") - 1) + constexpr auto size = funcname.size() - (sizeof("]") - 1); + std::array ret; + auto* const ret_data = ret.data(); + const auto* const funcname_data = funcname.data(); + for (std::size_t i = 0; i < size; ++i) + ret_data[i] = funcname_data[i]; + return ret; + } + + template + inline constexpr auto type_name = type_name_func(); + + + template + constexpr auto enum_in_array_name() noexcept + { + // constexpr auto f() [with auto _ = ( + //constexpr auto f() [with auto _ = (Scoped)0] + string_view s = __PRETTY_FUNCTION__ + + sizeof("constexpr auto enchantum::details::enum_in_array_name() [with auto Enum = ") - 1; + s.remove_suffix(sizeof("]") - 1); + + if constexpr (ScopedEnum) { + if (s.front() == '(') { + s.remove_prefix(1); + s.remove_suffix(sizeof(")0") - 1); + return s; + } + else { + return s.substr(0, s.rfind("::")); + } + } + else { + if (s.front() == '(') { + s.remove_prefix(1); + s.remove_suffix(sizeof(")0") - 1); + } + if (const auto pos = s.rfind("::"); pos != s.npos) + return s.substr(0, pos); + return string_view(); + } + } + + template + constexpr auto length_of_enum_in_template_array_if_casting() noexcept + { + if constexpr (ScopedEnum) { + return details::enum_in_array_name().size(); + } + else { + constexpr auto s = enum_in_array_name().size(); + constexpr auto& tyname = type_name; + constexpr auto str = string_view(tyname.data(), tyname.size()); + if (constexpr auto pos = str.rfind("::"); pos != str.npos) { + return s + str.substr(pos).size(); + } + else { + return s + tyname.size(); + } + } + } + + template + constexpr auto var_name() noexcept + { + //constexpr auto f() [with auto _ = std::array{std::__array_traits::_Type{a, b, c, e, d, (E)6}}] +#define SZC(x) (sizeof(x) - 1) + constexpr std::size_t funcsig_off = SZC("constexpr auto enchantum::details::var_name() [with auto ...Vs = {"); + return std::string_view(__PRETTY_FUNCTION__ + funcsig_off, SZC(__PRETTY_FUNCTION__) - funcsig_off - SZC("}]")); + } + + + template + inline constexpr auto static_storage_for = Copy; + + template + constexpr auto reflect() noexcept + { + constexpr auto Min = enum_traits::min; + constexpr auto Max = enum_traits::max; + + constexpr auto elements = []() { + constexpr auto length_of_enum_in_template_array_casting = details::length_of_enum_in_template_array_if_casting(); + constexpr auto Array = details::generate_arrays(); + auto str = [Array](std::index_sequence) { + return details::var_name(); + }(std::make_index_sequence()); + struct RetVal { + std::array pairs{}; + std::size_t total_string_length = 0; + std::size_t valid_count = 0; + } ret; + std::size_t index = 0; + constexpr auto enum_in_array_len = enum_in_array_name().size(); + while (index < Array.size()) { + if (str.front() == '(') { + str.remove_prefix(sizeof("(") - 1 + length_of_enum_in_template_array_casting + sizeof(")0") - + 1); // there is atleast 1 base 10 digit + //if(!str.empty()) + // std::cout << "after str \"" << str << '"' << '\n'; + + if (const auto commapos = str.find(','); commapos != str.npos) + str.remove_prefix(commapos + 2); + + //std::cout << "strsize \"" << str.size() << '"' << '\n'; + } + else { + if constexpr (enum_in_array_len != 0) + str.remove_prefix(enum_in_array_len + sizeof("::") - 1); + if constexpr (details::prefix_length_or_zero != 0) { + str.remove_prefix(details::prefix_length_or_zero); + } + const auto commapos = str.find(','); + + const auto name = str.substr(0, commapos); + + ret.pairs[ret.valid_count] = Pair{Array[index], name}; + ret.total_string_length += name.size() + ShouldNullTerminate; + + if (commapos != str.npos) + str.remove_prefix(commapos + 2); + ++ret.valid_count; + } + ++index; + } + return ret; + }(); + + constexpr auto strings = [elements]() { + std::array strings; + for (std::size_t _i = 0, index = 0; _i < elements.valid_count; ++_i) { + const auto& [_, s] = elements.pairs[_i]; + for (std::size_t i = 0; i < s.size(); ++i) + strings[index++] = s[i]; + + if constexpr (ShouldNullTerminate) + strings[index++] = '\0'; + } + return strings; + }(); + + std::array ret; + constexpr const auto* str = static_storage_for.data(); + for (std::size_t i = 0, string_index = 0; i < elements.valid_count; ++i) { + const auto& [e, s] = elements.pairs[i]; + auto& [re, rs] = ret[i]; + re = e; + + rs = {str + string_index, str + string_index + s.size()}; + string_index += s.size() + ShouldNullTerminate; + } + return ret; + } + +} // namespace details + +} // namespace enchantum \ No newline at end of file diff --git a/include/rfl/thirdparty/enchantum/details/enchantum_msvc.hpp b/include/rfl/thirdparty/enchantum/details/enchantum_msvc.hpp new file mode 100644 index 00000000..dc4b77ab --- /dev/null +++ b/include/rfl/thirdparty/enchantum/details/enchantum_msvc.hpp @@ -0,0 +1,161 @@ +#pragma once +#include "../common.hpp" +#include "generate_arrays.hpp" +#include "string_view.hpp" +#include +#include +#include +#include +#include + +namespace enchantum { + +namespace details { + +#define SZC(x) (sizeof(x) - 1) + template + constexpr auto type_name_func_size() noexcept + { + // (sizeof("auto __cdecl enchantum::details::type_name_func<") - 1) + return SZC(__FUNCSIG__) - SZC("auto __cdecl enchantum::details::type_name_func_size(void) noexcept"); + } + + template + constexpr auto enum_in_array_name() noexcept + { + string_view s = __FUNCSIG__ + sizeof("auto __cdecl enchantum::details::enum_in_array_name<") - 1; + s.remove_suffix(sizeof(">(void) noexcept") - 1); + + if constexpr (ScopedEnum) { + if (s.front() == '(') { + s.remove_prefix(sizeof("(enum ") - 1); + s.remove_suffix(sizeof(")0x0") - 1); + return s; + } + return s.substr(0, s.rfind("::")); + } + else { + if (s.front() == '(') { + s.remove_prefix(sizeof("(enum ") - 1); + s.remove_suffix(sizeof(")0x0") - 1); + } + if (const auto pos = s.rfind("::"); pos != s.npos) + return s.substr(0, pos); + return string_view(); + } + } + + template + constexpr auto var_name() noexcept + { + //auto __cdecl f{enum `anonymous-namespace'::UnscopedAnon + + using T = typename decltype(Array)::value_type; +#define SZC(x) (sizeof(x) - 1) + std::size_t funcsig_off = SZC("auto __cdecl enchantum::details::var_name(); + funcsig_off += type_name_len + SZC(","); + constexpr auto Size = Array.size(); + // clang-format off + funcsig_off += Size < 10 ? 1 + : Size < 100 ? 2 + : Size < 1000 ? 3 + : Size < 10000 ? 4 + : Size < 100000 ? 5 + : Size < 1000000 ? 6 + : Size < 10000000 ? 7 + : Size < 100000000 ? 8 + : Size < 1000000000 ? 9 + : 10; + // clang-format on + funcsig_off += SZC(">{enum ") + type_name_len; + return string_view(__FUNCSIG__ + funcsig_off, SZC(__FUNCSIG__) - funcsig_off - (sizeof("}>(void) noexcept") - 1)); + } +#undef SZC + + + template + inline constexpr auto static_storage_for = Copy; + + template + constexpr auto get_elements() + { + constexpr auto type_name_len = details::type_name_func_size(); + + auto str = var_name(); + struct RetVal { + std::array pairs{}; + std::size_t total_string_length = 0; + std::size_t valid_count = 0; + } ret; + std::size_t index = 0; + constexpr auto enum_in_array_len = details::enum_in_array_name().size(); + while (index < Array.size()) { + if (str.front() == '(') { + str.remove_prefix(sizeof("(enum ") - 1 + type_name_len + sizeof(")0x0") - 1); // there is atleast 1 base 16 hex digit + + if (const auto commapos = str.find(','); commapos != str.npos) + str.remove_prefix(commapos + 1); + } + else { + if constexpr (enum_in_array_len != 0) + str.remove_prefix(enum_in_array_len + sizeof("::") - 1); + + if constexpr (details::prefix_length_or_zero != 0) + str.remove_prefix(details::prefix_length_or_zero); + + const auto commapos = str.find(','); + + const auto name = str.substr(0, commapos); + + ret.pairs[ret.valid_count] = Pair{Array[index], name}; + ret.total_string_length += name.size() + ShouldNullTerminate; + + if (commapos != str.npos) + str.remove_prefix(commapos + 1); + ++ret.valid_count; + } + ++index; + } + return ret; + } + + template + constexpr auto reflect() noexcept + { + constexpr auto Min = enum_traits::min; + constexpr auto Max = enum_traits::max; + + constexpr auto elements = details::get_elements(), ShouldNullTerminate>(); + + constexpr auto strings = [elements]() { + std::array strings; + for (std::size_t _i = 0, index = 0; _i < elements.valid_count; ++_i) { + const auto& [_, s] = elements.pairs[_i]; + for (std::size_t i = 0; i < s.size(); ++i) + strings[index++] = s[i]; + + if constexpr (ShouldNullTerminate) + strings[index++] = '\0'; + } + return strings; + }(); + + std::array ret; + constexpr const auto* str = static_storage_for.data(); + for (std::size_t i = 0, string_index = 0; i < elements.valid_count; ++i) { + const auto& [e, s] = elements.pairs[i]; + auto& [re, rs] = ret[i]; + re = e; + + rs = {str + string_index, str + string_index + s.size()}; + string_index += s.size() + ShouldNullTerminate; + } + + return ret; + } +} // namespace details + + +} // namespace enchantum diff --git a/include/rfl/thirdparty/enchantum/details/format_util.hpp b/include/rfl/thirdparty/enchantum/details/format_util.hpp new file mode 100644 index 00000000..ac1c9798 --- /dev/null +++ b/include/rfl/thirdparty/enchantum/details/format_util.hpp @@ -0,0 +1,20 @@ +#include "../enchantum.hpp" +#include + +namespace enchantum { +namespace details { + template + std::string format(E e) noexcept + { + if constexpr (is_bitflag) { + if (const auto name = enchantum::to_string_bitflag(e); !name.empty()) + return std::string(name.data(), name.size()); + } + else { + if (const auto name = enchantum::to_string(e); !name.empty()) + return std::string(name.data(), name.size()); + } + return std::to_string(+enchantum::to_underlying(e)); // promote using + to select int overload if to underlying returns char + } +} // namespace details +} // namespace enchantum \ No newline at end of file diff --git a/include/rfl/thirdparty/enchantum/details/generate_arrays.hpp b/include/rfl/thirdparty/enchantum/details/generate_arrays.hpp new file mode 100644 index 00000000..503268ff --- /dev/null +++ b/include/rfl/thirdparty/enchantum/details/generate_arrays.hpp @@ -0,0 +1,63 @@ +#pragma once +#include "../common.hpp" +#include +#include +#include +#include + +namespace enchantum::details { + +template +inline constexpr std::size_t prefix_length_or_zero = 0; + +template +inline constexpr auto prefix_length_or_zero::prefix_length)> = std::size_t{ + enum_traits::prefix_length}; + +template +constexpr auto generate_arrays() +{ +#if defined __clang__ && __clang_major__ >= 20 + if constexpr (BitFlagEnum) { + if constexpr (EnumFixedUnderlying) { + constexpr std::size_t bits = sizeof(Enum) * CHAR_BIT; + std::array ret{}; // 0 value reflected + for (std::size_t i = 0; i < bits; ++i) + ret[i + 1] = static_cast(static_cast>>(1) << i); + return ret; + } + else { + constexpr auto bits = + []() { + auto copy = (Max > Min ? Max - Min : Min - Max); // handle negative; + std::size_t count = 0; + do + ++count; + while (copy >>= 1); + return count; + }(); + std::array b{}; // 0 value reflected + for (std::size_t i = 0; i < bits; ++i) + b[i + 1] = static_cast(static_cast>>(1) << i); + return b; + } + } +#else + if constexpr (BitFlagEnum) { + constexpr std::size_t bits = sizeof(Enum) * CHAR_BIT; + std::array ret{}; // 0 value reflected + for (std::size_t i = 0; i < bits; ++i) + ret[i + 1] = static_cast(static_cast>>(1) << i); + return ret; + } +#endif + else { + static_assert(Min < Max, "enum_traits::min must be less than enum_traits::max"); + std::array array; + auto* const array_data = array.data(); + for (std::size_t i = 0, size = array.size(); i < size; ++i) + array_data[i] = static_cast(static_cast(i) + Min); + return array; + } +} +} // namespace enchantum::details \ No newline at end of file diff --git a/include/rfl/thirdparty/enchantum/details/optional.hpp b/include/rfl/thirdparty/enchantum/details/optional.hpp new file mode 100644 index 00000000..16d35c15 --- /dev/null +++ b/include/rfl/thirdparty/enchantum/details/optional.hpp @@ -0,0 +1,19 @@ +#pragma once + +#ifdef ENCHANTUM_CONFIG_FILE + #include ENCHANTUM_CONFIG_FILE +#endif + +#ifndef ENCHANTUM_ALIAS_OPTIONAL + #include +#endif + + +namespace enchantum { +#ifdef ENCHANTUM_ALIAS_OPTIONAL +ENCHANTUM_ALIAS_OPTIONAL; +#else +using ::std::optional; +#endif + +} // namespace enchantum \ No newline at end of file diff --git a/include/rfl/thirdparty/enchantum/details/string_view.hpp b/include/rfl/thirdparty/enchantum/details/string_view.hpp new file mode 100644 index 00000000..61cb08ed --- /dev/null +++ b/include/rfl/thirdparty/enchantum/details/string_view.hpp @@ -0,0 +1,20 @@ +#pragma once + + +#ifdef ENCHANTUM_CONFIG_FILE + #include ENCHANTUM_CONFIG_FILE +#endif + +#ifndef ENCHANTUM_ALIAS_STRING_VIEW + #include +#endif + + +namespace enchantum { +#ifdef ENCHANTUM_ALIAS_STRING_VIEW +ENCHANTUM_ALIAS_STRING_VIEW; +#else +using ::std::string_view; +#endif + +} // namespace enchantum \ No newline at end of file diff --git a/include/rfl/thirdparty/enchantum/enchantum.hpp b/include/rfl/thirdparty/enchantum/enchantum.hpp new file mode 100644 index 00000000..63482ce6 --- /dev/null +++ b/include/rfl/thirdparty/enchantum/enchantum.hpp @@ -0,0 +1,199 @@ +#pragma once + +#include "common.hpp" +#include "details/optional.hpp" +#include "details/string_view.hpp" +#include "entries.hpp" +#include +#include + +namespace enchantum { + +template +inline constexpr bool has_zero_flag = false; + +template +inline constexpr bool has_zero_flag = []() { + for (const auto v : values) + if (static_cast>(v) == 0) + return true; + return false; +}(); + + +template +inline constexpr bool is_contiguous = false; + +template +inline constexpr bool is_contiguous = []() { + using T = std::underlying_type_t; + if constexpr (std::is_same_v) { + return true; + } + else { + constexpr auto& enums = entries; + for (std::size_t i = 0; i < enums.size() - 1; ++i) + if (T(enums[i].first) + 1 != T(enums[i + 1].first)) + return false; + return true; + } +}(); + + +template +concept ContiguousEnum = Enum && is_contiguous; + + +template +inline constexpr bool is_contiguous_bitflag = false; + +template +inline constexpr bool is_contiguous_bitflag = []() { + constexpr auto& enums = entries; + using T = std::underlying_type_t; + for (auto i = std::size_t{has_zero_flag}; i < enums.size() - 1; ++i) + if (T(enums[i].first) << 1 != T(enums[i + 1].first)) + return false; + return true; +}(); + +template +concept ContiguousBitFlagEnum = BitFlagEnum && is_contiguous_bitflag; + +template +[[nodiscard]] constexpr bool contains(const E value) noexcept +{ + for (const auto v : values) + if (v == value) + return true; + return false; +} + +template +[[nodiscard]] constexpr bool contains(const std::underlying_type_t value) noexcept +{ + return enchantum::contains(static_cast(value)); +} + + +template +[[nodiscard]] constexpr bool contains(const string_view name) noexcept +{ + for (const auto& s : names) + if (s == name) + return true; + return false; +} + + +template BinaryPredicate> +[[nodiscard]] constexpr bool contains(const string_view name, const BinaryPredicate binary_predicate) noexcept +{ + for (const auto& s : names) + if (binary_predicate(name, s)) + return true; + return false; +} + +template +[[nodiscard]] constexpr bool contains(const E value) noexcept +{ + using T = std::underlying_type_t; + return T(value) <= T(max) && T(value) >= T(min); +} + +namespace details { + template + struct index_to_enum_functor { + [[nodiscard]] constexpr optional operator()(const std::size_t index) const noexcept + { + optional ret; + if (index < values.size()) + ret.emplace(values[index]); + return ret; + } + }; + + struct enum_to_index_functor { + template + [[nodiscard]] constexpr optional operator()(const E e) const noexcept + { + if constexpr (ContiguousEnum) { + using T = std::underlying_type_t; + if (enchantum::contains(e)) + return optional(std::size_t(T(e) - T(min))); + } + else { + for (std::size_t i = 0; i < values.size(); ++i) + if (values[i] == e) + return optional(i); + } + return optional(); + } + }; + + + template + struct cast_functor { + [[nodiscard]] constexpr optional operator()(const std::underlying_type_t value) const noexcept + { + optional a; // rvo not that it really matters + if (!enchantum::contains(value)) + return a; + a.emplace(static_cast(value)); + return a; + } + + [[nodiscard]] constexpr optional operator()(const string_view name) const noexcept + { + optional a; // rvo not that it really matters + for (const auto& [e, s] : entries) { + if (s == name) { + a.emplace(e); + return a; + } + } + return a; // nullopt + } + + template BinaryPred> + [[nodiscard]] constexpr optional operator()(const string_view name, const BinaryPred binary_predicate) const noexcept + { + optional a; // rvo not that it really matters + for (const auto& [e, s] : entries) { + if (binary_predicate(name, s)) { + a.emplace(e); + return a; + } + } + return a; + } + }; + +} // namespace details + +template +inline constexpr details::index_to_enum_functor index_to_enum{}; + +inline constexpr details::enum_to_index_functor enum_to_index{}; + +template +inline constexpr details::cast_functor cast{}; + + +namespace details { + struct to_string_functor { + template + [[nodiscard]] constexpr string_view operator()(const E value) const noexcept + { + if (const auto i = enchantum::enum_to_index(value)) + return names[*i]; + return string_view(); + } + }; + +} // namespace details +inline constexpr details::to_string_functor to_string{}; + + +} // namespace enchantum \ No newline at end of file diff --git a/include/rfl/thirdparty/enchantum/entries.hpp b/include/rfl/thirdparty/enchantum/entries.hpp new file mode 100644 index 00000000..a1ab6ea7 --- /dev/null +++ b/include/rfl/thirdparty/enchantum/entries.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include "details/string_view.hpp" + +#if defined(__clang__) + #include "details/enchantum_clang.hpp" +#elif defined(__GNUC__) || defined(__GNUG__) + #include "details/enchantum_gcc.hpp" +#elif defined(_MSC_VER) + #include "details/enchantum_msvc.hpp" +#endif + +#include "common.hpp" +#include +#include + +namespace enchantum { + +#ifdef __cpp_lib_to_underlying +using ::std::to_underlying; +#else +template +[[nodiscard]] constexpr auto to_underlying(const E e) noexcept +{ + return static_cast>(e); +} +#endif + +template, bool ShouldNullTerminate = true> +inline constexpr auto entries = details::reflect, Pair, ShouldNullTerminate>(); + +template +inline constexpr auto values = []() { + constexpr auto& enums = entries; + std::array ret; + for (std::size_t i = 0; i < ret.size(); ++i) + ret[i] = enums[i].first; + return ret; +}(); + +template +inline constexpr auto names = []() { + constexpr auto& enums = entries, NullTerminated>; + std::array ret; + for (std::size_t i = 0; i < ret.size(); ++i) + ret[i] = enums[i].second; + return ret; +}(); + +template +inline constexpr auto min = entries.front().first; + +template +inline constexpr auto max = entries.back().first; + +template +inline constexpr std::size_t count = entries.size(); + +} // namespace enchantum \ No newline at end of file diff --git a/include/rfl/thirdparty/enchantum/fmt_format.hpp b/include/rfl/thirdparty/enchantum/fmt_format.hpp new file mode 100644 index 00000000..06d9615e --- /dev/null +++ b/include/rfl/thirdparty/enchantum/fmt_format.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include "bitflags.hpp" +#include "details/format_util.hpp" +#include "enchantum.hpp" +#include + +template +struct fmt::formatter : fmt::formatter { + template + constexpr auto format(const E e, FmtContext& ctx) const + { + return fmt::formatter::format(enchantum::details::format(e), ctx); + } +}; \ No newline at end of file diff --git a/include/rfl/thirdparty/enchantum/iostream.hpp b/include/rfl/thirdparty/enchantum/iostream.hpp new file mode 100644 index 00000000..faf2cb47 --- /dev/null +++ b/include/rfl/thirdparty/enchantum/iostream.hpp @@ -0,0 +1,8 @@ +#pragma once +#include "istream.hpp" +#include "ostream.hpp" + +namespace enchantum::iostream_operators { +using ::enchantum::istream_operators::operator>>; +using ::enchantum::ostream_operators::operator<<; +} // namespace enchantum::iostream_operators \ No newline at end of file diff --git a/include/rfl/thirdparty/enchantum/istream.hpp b/include/rfl/thirdparty/enchantum/istream.hpp new file mode 100644 index 00000000..b003279f --- /dev/null +++ b/include/rfl/thirdparty/enchantum/istream.hpp @@ -0,0 +1,32 @@ +#pragma once +#include "enchantum.hpp" +#include +#include +#include + +namespace enchantum::istream_operators { +template + requires std::assignable_from +std::basic_istream& operator>>(std::basic_istream& is, E& value) +{ + std::basic_string s; + is >> s; + if (!is) + return is; + + if constexpr (is_bitflag) { + if (const auto v = enchantum::cast_bitflag(s)) + value = *v; + else + is.setstate(std::ios_base::failbit); + } + else { + if (const auto v = enchantum::cast(s)) + value = *v; + else + is.setstate(std::ios_base::failbit); + } + return is; +} + +} // namespace enchantum::istream_operators diff --git a/include/rfl/thirdparty/enchantum/next_value.hpp b/include/rfl/thirdparty/enchantum/next_value.hpp new file mode 100644 index 00000000..339d3081 --- /dev/null +++ b/include/rfl/thirdparty/enchantum/next_value.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include "common.hpp" +#include "details/optional.hpp" +#include "enchantum.hpp" +#include + +namespace enchantum { +namespace details { + template + struct next_value_functor { + template + [[nodiscard]] constexpr optional operator()(const E value, const std::ptrdiff_t n = 1) const noexcept + { + if (!enchantum::contains(value)) + return optional{}; + + const auto index = static_cast(*enchantum::enum_to_index(value)) + (n * N); + if (index >= 0 && index < static_cast(count)) + return optional{values[static_cast(index)]}; + return optional{}; + } + }; + + template + struct next_value_circular_functor { + template + [[nodiscard]] constexpr E operator()(const E value, const std::ptrdiff_t n = 1) const noexcept + { + ENCHANTUM_ASSERT(enchantum::contains(value), "next/prev_value_circular requires 'value' to be a valid enum member", value); + const auto i = static_cast(*enchantum::enum_to_index(value)); + constexpr auto count = static_cast(enchantum::count); + return values[static_cast(((i + (n * N)) % count + count) % count)]; // handles wrap around and negative n + } + }; +} // namespace details + + +inline constexpr details::next_value_functor<1> next_value{}; +inline constexpr details::next_value_functor<-1> prev_value{}; +inline constexpr details::next_value_circular_functor<1> next_value_circular{}; +inline constexpr details::next_value_circular_functor<-1> prev_value_circular{}; + +} // namespace enchantum diff --git a/include/rfl/thirdparty/enchantum/ostream.hpp b/include/rfl/thirdparty/enchantum/ostream.hpp new file mode 100644 index 00000000..0d69acfa --- /dev/null +++ b/include/rfl/thirdparty/enchantum/ostream.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include "enchantum.hpp" +#include +#include +#include "details/format_util.hpp" + +namespace enchantum::ostream_operators { +template +std::basic_ostream& operator<<(std::basic_ostream& os, const E e) +{ + return os << details::format(e); +} +} // namespace enchantum::ostream_operators \ No newline at end of file diff --git a/include/rfl/thirdparty/enchantum/std_format.hpp b/include/rfl/thirdparty/enchantum/std_format.hpp new file mode 100644 index 00000000..9c2b9e5e --- /dev/null +++ b/include/rfl/thirdparty/enchantum/std_format.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include "bitflags.hpp" +#include "enchantum.hpp" + +#include +#include +#include "details/format_util.hpp" + +template +struct std::formatter : std::formatter { + template + constexpr auto format(const E e, FmtContext& ctx) const + { + return std::formatter::format(enchantum::details::format(e), ctx); + } +}; \ No newline at end of file