#ifndef RFL_INTERNAL_ENUMS_GET_ENUM_NAMES_HPP_ #define RFL_INTERNAL_ENUMS_GET_ENUM_NAMES_HPP_ #include #include #include #include "../../Literal.hpp" #include "../../define_literal.hpp" #include "../../internal/remove_namespaces.hpp" #include "Names.hpp" #include "is_scoped_enum.hpp" // https://en.cppreference.com/w/cpp/language/static_cast: // 8) A value of integer or enumeration type can be converted to any complete // enumeration type. // If the underlying type is not fixed, the behavior is undefined if the value // of expression is out of range (the range is all values possible for the // smallest bit-field large enough to hold all enumerators of the target // enumeration). If the underlying type is fixed, the result is the same as // converting the original value first to the underlying type of the enumeration // and then to the enumeration type. // https://en.cppreference.com/w/cpp/language/enum // enum struct|class name { enumerator = constexpr , enumerator = constexpr , // ... } (1) // ... // 1) declares a scoped enumeration type whose underlying type is int (the // keywords class and struct are exactly equivalent) // // --> These rules taken together imply that if you EITHER fix the type OR you // use a scoped integer, static_cast(some_integer_value) will always be // defined. namespace rfl { namespace internal { namespace enums { template consteval auto get_enum_name_str_view() { // Unfortunately, we cannot avoid the use of a compiler-specific macro for // Clang on Windows. For all other compilers, function_name works as intended. #if defined(__clang__) && defined(_MSC_VER) const auto func_name = std::string_view{__PRETTY_FUNCTION__}; #else const auto func_name = std::string_view{std::source_location::current().function_name()}; #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 T calc_greatest_power_of_two() { if constexpr (std::is_signed_v) { return static_cast(1) << (sizeof(T) * 8 - 2); } else { return static_cast(1) << (sizeof(T) * 8 - 1); } } template consteval T get_max() { if constexpr (_is_flag) { return calc_greatest_power_of_two(); } else { return std::numeric_limits::max() > 127 ? static_cast(127) : std::numeric_limits::max(); } } template consteval T calc_j() { if constexpr (_is_flag) { return static_cast(1) << _i; } else { return static_cast(_i); } } template consteval auto get_enum_names_impl() { 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_) == '(') { if constexpr (j == _max) { return NamesType{}; } else { return get_enum_names_impl(); } } else { using NewNames = typename NamesType::template AddOneType< Literal()>, static_cast(j)>; if constexpr (j == _max) { return NewNames{}; } else { return get_enum_names_impl(); } } } 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_max, _is_flag>(); using EmptyNames = Names, 0>; return get_enum_names_impl(); } } // namespace enums } // namespace internal } // namespace rfl #endif