diff --git a/include/nameof.hpp b/include/nameof.hpp index 7bf4423..113bf37 100644 --- a/include/nameof.hpp +++ b/include/nameof.hpp @@ -364,15 +364,41 @@ inline constexpr auto enum_name_v = n(); namespace enums { -template -inline constexpr int reflected_min_v = static_cast(enum_range::min > (std::numeric_limits>::min)() - ? enum_range::min - : (std::numeric_limits>::min)()); +template +constexpr bool mixed_sign_less(L lhs, R rhs) noexcept { + static_assert(std::is_integral_v && std::is_integral_v, "nameof::detail::mixed_sign_less requires integral type."); + + if constexpr (std::is_signed_v && std::is_unsigned_v) { + // If 'left' is negative, then result is 'true', otherwise cast & compare. + return lhs < 0 || static_cast>(lhs) < rhs; + } else if constexpr (std::is_unsigned_v && std::is_signed_v) { + // If 'right' is negative, then result is 'false', otherwise cast & compare. + return rhs >= 0 && lhs < static_cast>(rhs); + } else { + // If same signedness (both signed or both unsigned). + return lhs < rhs; + } +} + +template +constexpr int mixed_sign_min_as_int(L lhs, R rhs) noexcept { + static_assert(std::is_integral_v && std::is_integral_v, "nameof::detail::mixed_sign_min_as_int requires integral type."); + + return mixed_sign_less(lhs, rhs) ? static_cast(lhs) : static_cast(rhs); +} + +template +constexpr int mixed_sign_max_as_int(L lhs, R rhs) noexcept { + static_assert(std::is_integral_v && std::is_integral_v, "nameof::detail::mixed_sign_max_as_int requires integral type."); + + return mixed_sign_less(lhs, rhs) ? static_cast(rhs) : static_cast(lhs); +} template -inline constexpr int reflected_max_v = static_cast(enum_range::max < (std::numeric_limits>::max)() - ? enum_range::max - : (std::numeric_limits>::max)()); +inline constexpr int reflected_min_v = mixed_sign_max_as_int(enum_range::min, (std::numeric_limits>::min)()); + +template +inline constexpr int reflected_max_v = mixed_sign_min_as_int(enum_range::max, (std::numeric_limits>::max)()); template constexpr std::size_t reflected_size() { @@ -388,28 +414,32 @@ constexpr std::size_t reflected_size() { } template -constexpr int range_min(std::integer_sequence) noexcept { - static_assert(is_enum_v, "nameof::detail::range_min requires enum type."); +constexpr auto values(std::integer_sequence) noexcept { + static_assert(is_enum_v, "nameof::detail::values requires enum type."); + constexpr std::array valid{{(n(I + reflected_min_v)>().size() != 0)...}}; + constexpr std::size_t count = ((valid[I] ? 1 : 0) + ...); - int r = 0; - (void)(((n(I + reflected_min_v)>().size() != 0) ? (r = I + reflected_min_v, false) : true) && ...); - return r; -} + std::array values{}; + for (std::size_t i = 0, v = 0; v < count; ++i) { + if (valid[i]) { + values[v++] = static_cast(static_cast(i) + reflected_min_v); + } + } -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; + return values; } template -inline constexpr int min_v = range_min(std::make_integer_sequence()>{}); +inline constexpr auto values_v = values(std::make_integer_sequence()>{}); template -inline constexpr int max_v = range_max(std::make_integer_sequence()>{}); +inline constexpr std::size_t count_v = values_v.size(); + +template +inline constexpr int min_v = values_v.empty() ? 0 : static_cast(values_v.front()); + +template +inline constexpr int max_v = values_v.empty() ? 0 : static_cast(values_v.back()); template constexpr std::size_t range_size() noexcept { @@ -422,41 +452,10 @@ constexpr std::size_t range_size() noexcept { } template -inline constexpr std::size_t size_v = range_size(); +inline constexpr std::size_t range_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>; +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)(); @@ -466,25 +465,24 @@ 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)...}}; + return std::array, sizeof...(I)>{{((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); +inline constexpr bool sparsity_v = (sizeof(const char*) * range_size_v) > (sizeof(index_t) * range_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()...}}; + 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()...}}; + return std::array{{enum_name_v[I]>.data()...}}; } template @@ -492,9 +490,9 @@ constexpr auto strings() noexcept { static_assert(is_enum_v, "nameof::detail::strings requires enum type."); if constexpr (sparsity_v) { - return strings(sequence_v); + return strings(std::make_index_sequence>{}); } else { - return strings(range_v); + return strings(std::make_integer_sequence>{}); } } @@ -507,7 +505,7 @@ class enum_traits { 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); + inline static constexpr auto indexes_ = indexes(std::make_integer_sequence>{}); public: static constexpr std::string_view name(E value) noexcept {