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: 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 b7a889b3..f2cab910 100644 --- a/include/rfl/enums.hpp +++ b/include/rfl/enums.hpp @@ -4,69 +4,113 @@ #include #include "Result.hpp" -#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 "internal/strings/strings.hpp" +#include "thirdparty/enchantum/enchantum.hpp" +#include "thirdparty/enchantum/bitflags.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); +template +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); +template +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 +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 +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 +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 +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 +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(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 332c2ed8..16a3d18b 100644 --- a/include/rfl/internal/enums/Names.hpp +++ b/include/rfl/internal/enums/Names.hpp @@ -9,63 +9,17 @@ #include #include "../../Literal.hpp" +#include "../../config.hpp" #include "../../define_literal.hpp" #include "../../make_named_tuple.hpp" +#include "../../thirdparty/enchantum/enchantum.hpp" #include "../StringLiteral.hpp" -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 { - -// 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 { - -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> {}; +namespace rfl::internal::enums { template struct Names { + auto... _enums> +struct Names { /// Contains a collection of enums as compile-time strings. using Literal = LiteralType; @@ -73,30 +27,9 @@ template enums_ = - std::array{_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{}; -} - template auto names_to_enumerator_named_tuple( @@ -132,8 +65,7 @@ names_to_underlying_enumerator_array( static_cast>(_enums))...}; } -} // namespace enums -} // namespace internal -} // namespace rfl +} // namespace rfl::internal::enums #endif + diff --git a/include/rfl/internal/enums/StringConverter.hpp b/include/rfl/internal/enums/StringConverter.hpp deleted file mode 100644 index b2f3d1b6..00000000 --- a/include/rfl/internal/enums/StringConverter.hpp +++ /dev/null @@ -1,127 +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 "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 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)); - } - - /// 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 = 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()); - } - } - } - - /// 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 d5a31569..724837ee 100644 --- a/include/rfl/internal/enums/get_enum_names.hpp +++ b/include/rfl/internal/enums/get_enum_names.hpp @@ -1,19 +1,41 @@ #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 -#if __has_include() -#include -#endif - #include "../../Literal.hpp" -#include "../../define_literal.hpp" -#include "../../internal/remove_namespaces.hpp" +#include "../../thirdparty/enchantum/enchantum.hpp" #include "Names.hpp" -#include "is_scoped_enum.hpp" +#include "range_defined.hpp" // https://en.cppreference.com/w/cpp/language/static_cast: @@ -39,147 +61,70 @@ // use a scoped integer, static_cast(some_integer_value) will always be // defined. -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; - 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 + requires requires(E e) { + { e | e } -> std::same_as; } -} +constexpr inline bool enchantum::is_bitflag = true; -template -consteval T calc_j() { - if constexpr (_is_flag) { - return static_cast(1) << _i; - } else { - return static_cast(_i); - } -} +// Specialize the enchantum EnumTraits further, so rfl::config::enum_range +// works. +namespace enchantum { -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 + requires rfl::internal::enums::range_defined +struct enum_traits { + static constexpr std::size_t prefix_length = 0; -template -struct NamesCombiner { - template - static consteval auto combine(std::integer_sequence) { - return (Names{} | ... | - std::integral_constant()); - } + static constexpr auto min = rfl::config::enum_range::min; + static constexpr auto max = rfl::config::enum_range::max; }; -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; - } - } +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 -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!"); +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; +}; - constexpr auto max = get_range_max(); - constexpr auto min = get_range_min(); - constexpr auto range_size = max - min + 1; +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; +}; - static_assert(range_size > 0, - "enum_range requires a valid range size. Ensure that max is " - "greater than min."); +} // namespace enchantum - using EmptyNames = Names, 0, _is_flag>; +namespace rfl::internal::enums { - return CombineNames<>::template apply(); +template +consteval auto get_enum_names() { + 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{})...>, + entries.size(), enchantum::is_bitflag, + entries[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 deleted file mode 100644 index 89369039..00000000 --- a/include/rfl/internal/enums/is_flag_enum.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef RFL_INTERNAL_ENUMS_IS_FLAG_ENUM_HPP_ -#define RFL_INTERNAL_ENUMS_IS_FLAG_ENUM_HPP_ - -#include - -#include "is_scoped_enum.hpp" - -namespace rfl { -namespace internal { -namespace enums { - -template -concept is_flag_enum = is_scoped_enum && - requires(EnumType e1, EnumType e2) { - { e1 | e2 } -> std::same_as; -}; - -} // namespace enums -} // namespace internal -} // namespace rfl - -#endif 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 958aca8a..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 - -namespace rfl { -namespace internal { -namespace enums { - -template -concept is_scoped_enum = std::is_enum_v && - !std::is_convertible_v>; - -} // namespace enums -} // namespace internal -} // namespace rfl - -#endif 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/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); } } 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 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); } 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