diff --git a/include/nameof.hpp b/include/nameof.hpp index b98e2ac..30f11b3 100644 --- a/include/nameof.hpp +++ b/include/nameof.hpp @@ -36,8 +36,9 @@ #include #include #include -#include #include +#include +#include // Enum value must be greater or equals than NAMEOF_ENUM_RANGE_MIN. By default NAMEOF_ENUM_RANGE_MIN = -128. // If need another min range for all enum types by default, redefine the macro NAMEOF_ENUM_RANGE_MIN. @@ -59,23 +60,18 @@ namespace nameof { template struct enum_range final { static_assert(std::is_enum_v, "nameof::enum_range requires enum type."); - static constexpr int min = NAMEOF_ENUM_RANGE_MIN; - static constexpr int max = NAMEOF_ENUM_RANGE_MAX; + inline static constexpr int min = NAMEOF_ENUM_RANGE_MIN; + inline static constexpr int max = NAMEOF_ENUM_RANGE_MAX; static_assert(max > min, "nameof::enum_range requires max > min."); }; -static_assert(NAMEOF_ENUM_RANGE_MIN <= 0, - "NAMEOF_ENUM_RANGE_MIN must be less or equals than 0."); -static_assert(NAMEOF_ENUM_RANGE_MIN > (std::numeric_limits::min)(), - "NAMEOF_ENUM_RANGE_MIN must be greater than INT_MIN."); +static_assert(NAMEOF_ENUM_RANGE_MIN <= 0, "NAMEOF_ENUM_RANGE_MIN must be less or equals than 0."); +static_assert(NAMEOF_ENUM_RANGE_MIN > (std::numeric_limits::min)(), "NAMEOF_ENUM_RANGE_MIN must be greater than INT16_MIN."); -static_assert(NAMEOF_ENUM_RANGE_MAX > 0, - "NAMEOF_ENUM_RANGE_MAX must be greater than 0."); -static_assert(NAMEOF_ENUM_RANGE_MAX < (std::numeric_limits::max)(), - "NAMEOF_ENUM_RANGE_MAX must be less than INT_MAX."); +static_assert(NAMEOF_ENUM_RANGE_MAX > 0, "NAMEOF_ENUM_RANGE_MAX must be greater than 0."); +static_assert(NAMEOF_ENUM_RANGE_MAX < (std::numeric_limits::max)(), "NAMEOF_ENUM_RANGE_MAX must be less than INT16_MAX."); -static_assert(NAMEOF_ENUM_RANGE_MAX > NAMEOF_ENUM_RANGE_MIN, - "NAMEOF_ENUM_RANGE_MAX must be greater than NAMEOF_ENUM_RANGE_MIN."); +static_assert(NAMEOF_ENUM_RANGE_MAX > NAMEOF_ENUM_RANGE_MIN, "NAMEOF_ENUM_RANGE_MAX must be greater than NAMEOF_ENUM_RANGE_MIN."); namespace detail { @@ -321,30 +317,189 @@ constexpr std::string_view pretty_name(std::string_view name, bool remove_templa template constexpr auto n() noexcept { static_assert(std::is_enum_v, "nameof::detail::n requires enum type."); -#if defined(__clang__) || defined(__GNUC__) && __GNUC__ >= 9 +#if defined(__clang__) || defined(__GNUC__) constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); #elif defined(_MSC_VER) constexpr auto name = pretty_name({__FUNCSIG__, sizeof(__FUNCSIG__) - 17}); -#else - static_assert(nameof_enum_supported::value, "nameof::nameof_enum: Unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); - return std::string_view{}; // Unsupported compiler. #endif -#if defined(__clang__) || defined(__GNUC__) && __GNUC__ >= 9 || defined(_MSC_VER) - return static_string{name}; -#endif + if constexpr (nameof_enum_supported::value) { + return static_string{name}; + } else { + static_assert(nameof_enum_supported::value, "nameof::nameof_enum: Unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + return std::string_view{}; // Unsupported compiler. + } } template -inline constexpr auto nameof_enum_v = n(); +inline constexpr auto enum_name_v = n(); -template -constexpr auto enum_names(std::integer_sequence) noexcept { - static_assert(std::is_enum_v, "nameof::detail::enum_names requires enum type."); +namespace enums { - return std::array{{nameof_enum_v(I + O)>...}}; +template +inline constexpr bool is_enum_v = std::is_enum_v && std::is_same_v>; + +template +inline constexpr int reflected_min_v = static_cast(enum_range::min > (std::numeric_limits>::min)() + ? enum_range::min + : (std::numeric_limits>::min)()); + +template +inline constexpr int reflected_max_v = static_cast(enum_range::max < (std::numeric_limits>::max)() + ? enum_range::max + : (std::numeric_limits>::max)()); + +template +constexpr std::size_t reflected_size() { + static_assert(is_enum_v, "nameof::detail::range_size requires enum type."); + static_assert(reflected_min_v > (std::numeric_limits::min)(), "nameof::enum_range requires min must be greater than INT16_MIN."); + static_assert(reflected_max_v < (std::numeric_limits::max)(), "nameof::enum_range requires max must be less than INT16_MAX."); + static_assert(reflected_max_v > reflected_min_v, "nameof::enum_range requires max > min."); + constexpr auto size = reflected_max_v - reflected_min_v + 1; + static_assert(size > 0, "nameof::enum_range requires valid size."); + + return static_cast(size); } +template +constexpr int range_min(std::integer_sequence) noexcept { + static_assert(is_enum_v, "nameof::detail::range_min requires enum type."); + + int r = 0; + (void)(((n(I + reflected_min_v)>().size() != 0) ? (r = I + reflected_min_v, false) : true) && ...); + return r; +} + +template +constexpr int range_max(std::integer_sequence) noexcept { + static_assert(is_enum_v, "nameof::detail::range_max requires enum type."); + + int r = 0; + (void)(((n(reflected_max_v - I)>().size() != 0) ? (r = reflected_max_v - I, false) : true) && ...); + return r; +} + +template +inline constexpr int min_v = range_min(std::make_integer_sequence()>{}); + +template +inline constexpr int max_v = range_max(std::make_integer_sequence()>{}); + +template +constexpr std::size_t range_size() noexcept { + static_assert(is_enum_v, "nameof::detail::range_size requires enum type."); + constexpr auto size = max_v - min_v + 1; + static_assert(size > 0, "nameof::enum_range requires valid size."); + + return static_cast(size); +} + +template +inline constexpr std::size_t size_v = range_size(); + +template +inline constexpr auto range_v = std::make_integer_sequence>{}; + +template +constexpr std::size_t count(std::integer_sequence) noexcept { + static_assert(is_enum_v, "nameof::detail::count requires enum type."); + + return (((n(I + min_v)>().size() != 0) ? 1 : 0) + ...); +} + +template +inline constexpr std::size_t count_v = count(range_v); + +template +inline constexpr auto sequence_v = std::make_index_sequence>{}; + +template +constexpr auto values(std::integer_sequence) noexcept { + static_assert(is_enum_v, "nameof::detail::values requires enum type."); + constexpr std::array> valid{{(n(I + min_v)>().size() != 0)...}}; + + std::array> values{}; + for (std::size_t i = 0, v = 0; v < count_v; ++i) { + if (valid[i]) { + values[v++] = static_cast(static_cast(i) + min_v); + } + } + + return values; +} + +template +using index_t = std::conditional_t < (std::numeric_limits::max)(), std::uint8_t, std::uint16_t>; + +template +inline constexpr auto invalid_index_v = (std::numeric_limits>::max)(); + +template +constexpr auto indexes(std::integer_sequence) noexcept { + static_assert(is_enum_v, "nameof::detail::indexes requires enum type."); + index_t i = 0; + + return std::array, size_v>{{((n(I + min_v)>().size() != 0) ? i++ : invalid_index_v)...}}; +} + +template +inline constexpr bool sparsity_v = (sizeof(const char*) * size_v) > (sizeof(index_t) * size_v + sizeof(const char*) * count_v); + +template +constexpr auto strings(std::integer_sequence) noexcept { + static_assert(is_enum_v, "nameof::detail::strings requires enum type."); + + return std::array>{{enum_name_v(I + min_v)>.data()...}}; +} + +template +constexpr auto strings(std::index_sequence) noexcept { + static_assert(is_enum_v, "nameof::detail::strings requires enum type."); + constexpr auto vals = values(range_v); + + return std::array>{{enum_name_v.data()...}}; +} + +template +constexpr auto strings() noexcept { + static_assert(is_enum_v, "nameof::detail::strings requires enum type."); + + if constexpr (sparsity_v) { + return strings(std::make_index_sequence>{}); + } else { + return strings(range_v); + } +} + +template +struct enum_traits { + static constexpr std::string_view name(E value) noexcept { + if (static_cast(value) >= static_cast(min_v) && static_cast(value) <= static_cast(max_v)) { + if constexpr (sparsity_v) { + if (auto i = indexes[static_cast(value) - min_v]; i != invalid_index_v) { + return strings[i]; + } + } else { + return strings[static_cast(value) - min_v]; + } + } + + return {}; // Value out of range. + } + + private: + static_assert(is_enum_v, "nameof::enum_traits requires enum type."); + static_assert(enum_range::min > (std::numeric_limits::min)(), "nameof::enum_range requires min must be greater than INT16_MIN."); + static_assert(enum_range::max < (std::numeric_limits::max)(), "nameof::enum_range requires max must be less than INT16_MAX."); + static_assert(enum_range::max > enum_range::min, "nameof::enum_range requires max > min."); + static_assert(count_v > 0, "nameof::enum_range requires enum implementation or valid max and min."); + using U = std::underlying_type_t; + inline static constexpr auto strings = strings(); + inline static constexpr auto indexes = indexes(range_v); +}; + +} // namespace nameof::detail::enums + template constexpr auto n() noexcept { #if defined(__clang__) @@ -374,31 +529,14 @@ inline constexpr bool is_nameof_enum_supported = detail::nameof_enum_supported [[nodiscard]] constexpr auto nameof_enum(E value) noexcept -> detail::enable_if_enum_t { - using D = detail::remove_cvref_t; - static_assert(enum_range::min > (std::numeric_limits::min)(), "nameof::enum_range requires min must be greater than INT_MIN."); - static_assert(enum_range::max < (std::numeric_limits::max)(), "nameof::enum_range requires max must be less than INT_MAX."); - static_assert(enum_range::max > enum_range::min, "nameof::enum_range requires max > min."); - using U = std::underlying_type_t; - constexpr int max = static_cast(enum_range::max < (std::numeric_limits::max)() ? enum_range::max : (std::numeric_limits::max)()); - constexpr int min = static_cast(enum_range::min > (std::numeric_limits::min)() ? enum_range::min : (std::numeric_limits::min)()); - constexpr auto names = detail::enum_names(std::make_integer_sequence{}); - - if (static_cast(value) > static_cast(max) || static_cast(value) < static_cast(min)) { - return {}; // Value out of range. - } - - if (auto i = static_cast(static_cast(value) - min); i < names.size()) { - return names[i]; - } - - return {}; // Value out of range. + return detail::enums::enum_traits>::name(value); } // Obtains simple (unqualified) string enum name of static storage enum variable. // This version is much lighter on the compile times and is not restricted to the enum_range limitation. template [[nodiscard]] constexpr auto nameof_enum() noexcept -> detail::enable_if_enum_t { - return detail::nameof_enum_v, V>; + return detail::enum_name_v, V>; } // Obtains string name of type, reference and cv-qualifiers are ignored.