From c9ae9a57c3955c9e36afad72102e2252d44c0676 Mon Sep 17 00:00:00 2001 From: Daniil Goncharov Date: Fri, 1 Dec 2023 01:33:57 +0400 Subject: [PATCH] add support big enum range (#65) --- include/nameof.hpp | 211 +++++++++++++++++++----------------------- test/test_aliases.cpp | 2 +- 2 files changed, 96 insertions(+), 117 deletions(-) diff --git a/include/nameof.hpp b/include/nameof.hpp index 4f7976a..fb9f4e2 100644 --- a/include/nameof.hpp +++ b/include/nameof.hpp @@ -496,10 +496,14 @@ constexpr string_view pretty_name(string_view name, bool remove_suffix = true) n return {}; // Invalid name. } +#if defined(__cpp_lib_array_constexpr) && __cpp_lib_array_constexpr >= 201603L +# define NAMEOF_ARRAY_CONSTEXPR 1 +#else template -constexpr std::array, N> to_array(T (&a)[N], std::index_sequence) { +constexpr std::array, N> to_array(T (&a)[N], std::index_sequence) noexcept { return {{a[I]...}}; } +#endif template constexpr bool cmp_less(L lhs, R rhs) noexcept { @@ -561,19 +565,19 @@ constexpr auto n() noexcept { #else constexpr auto name = string_view{}; #endif - return cstring{name}; + return name; } else { - return cstring<0>{}; + return string_view{}; } } template constexpr auto enum_name() noexcept { - static_assert(is_enum_v, "nameof::detail::n requires enum type."); [[maybe_unused]] constexpr auto custom_name = customize::enum_name(V); if constexpr (custom_name.empty()) { - return n(); + constexpr auto name = n(); + return cstring{name}; } else { return cstring{custom_name}; } @@ -584,26 +588,22 @@ inline constexpr auto enum_name_v = enum_name(); template constexpr bool is_valid() noexcept { - static_assert(is_enum_v, "nameof::detail::is_valid requires enum type."); - #if defined(__clang__) && __clang_major__ >= 16 // https://reviews.llvm.org/D130058, https://reviews.llvm.org/D131307 constexpr E v = __builtin_bit_cast(E, V); +#else + constexpr E v = static_cast(V); +#endif [[maybe_unused]] constexpr auto custom_name = customize::enum_name(v); if constexpr (custom_name.empty()) { return n().size() != 0; } else { return custom_name.size() != 0; } -#else - return enum_name(V)>().size() != 0; -#endif } template > constexpr U ualue(std::size_t i) noexcept { - static_assert(is_enum_v, "nameof::detail::ualue requires enum type."); - if constexpr (std::is_same_v) { // bool special case static_assert(O == 0, "nameof::detail::ualue requires valid offset."); @@ -617,15 +617,11 @@ constexpr U ualue(std::size_t i) noexcept { template > constexpr E value(std::size_t i) noexcept { - static_assert(is_enum_v, "nameof::detail::value requires enum type."); - return static_cast(ualue(i)); } template > constexpr int reflected_min() noexcept { - static_assert(is_enum_v, "nameof::detail::reflected_min requires enum type."); - if constexpr (IsFlags) { return 0; } else { @@ -642,8 +638,6 @@ constexpr int reflected_min() noexcept { template > constexpr int reflected_max() noexcept { - static_assert(is_enum_v, "nameof::detail::reflected_max requires enum type."); - if constexpr (IsFlags) { return std::numeric_limits::digits - 1; } else { @@ -658,39 +652,67 @@ constexpr int reflected_max() noexcept { } } -template -inline constexpr auto reflected_min_v = reflected_min(); +#define NAMEOF_FOR_EACH_256(T) \ + T( 0)T( 1)T( 2)T( 3)T( 4)T( 5)T( 6)T( 7)T( 8)T( 9)T( 10)T( 11)T( 12)T( 13)T( 14)T( 15)T( 16)T( 17)T( 18)T( 19)T( 20)T( 21)T( 22)T( 23)T( 24)T( 25)T( 26)T( 27)T( 28)T( 29)T( 30)T( 31) \ + T( 32)T( 33)T( 34)T( 35)T( 36)T( 37)T( 38)T( 39)T( 40)T( 41)T( 42)T( 43)T( 44)T( 45)T( 46)T( 47)T( 48)T( 49)T( 50)T( 51)T( 52)T( 53)T( 54)T( 55)T( 56)T( 57)T( 58)T( 59)T( 60)T( 61)T( 62)T( 63) \ + T( 64)T( 65)T( 66)T( 67)T( 68)T( 69)T( 70)T( 71)T( 72)T( 73)T( 74)T( 75)T( 76)T( 77)T( 78)T( 79)T( 80)T( 81)T( 82)T( 83)T( 84)T( 85)T( 86)T( 87)T( 88)T( 89)T( 90)T( 91)T( 92)T( 93)T( 94)T( 95) \ + T( 96)T( 97)T( 98)T( 99)T(100)T(101)T(102)T(103)T(104)T(105)T(106)T(107)T(108)T(109)T(110)T(111)T(112)T(113)T(114)T(115)T(116)T(117)T(118)T(119)T(120)T(121)T(122)T(123)T(124)T(125)T(126)T(127) \ + T(128)T(129)T(130)T(131)T(132)T(133)T(134)T(135)T(136)T(137)T(138)T(139)T(140)T(141)T(142)T(143)T(144)T(145)T(146)T(147)T(148)T(149)T(150)T(151)T(152)T(153)T(154)T(155)T(156)T(157)T(158)T(159) \ + T(160)T(161)T(162)T(163)T(164)T(165)T(166)T(167)T(168)T(169)T(170)T(171)T(172)T(173)T(174)T(175)T(176)T(177)T(178)T(179)T(180)T(181)T(182)T(183)T(184)T(185)T(186)T(187)T(188)T(189)T(190)T(191) \ + T(192)T(193)T(194)T(195)T(196)T(197)T(198)T(199)T(200)T(201)T(202)T(203)T(204)T(205)T(206)T(207)T(208)T(209)T(210)T(211)T(212)T(213)T(214)T(215)T(216)T(217)T(218)T(219)T(220)T(221)T(222)T(223) \ + T(224)T(225)T(226)T(227)T(228)T(229)T(230)T(231)T(232)T(233)T(234)T(235)T(236)T(237)T(238)T(239)T(240)T(241)T(242)T(243)T(244)T(245)T(246)T(247)T(248)T(249)T(250)T(251)T(252)T(253)T(254)T(255) -template -inline constexpr auto reflected_max_v = reflected_max(); - -template -constexpr std::size_t values_count(const bool (&valid)[N]) noexcept { - auto count = std::size_t{0}; - for (std::size_t i = 0; i < N; ++i) { - if (valid[i]) { - ++count; - } +template +constexpr void valid_count(bool* valid, std::size_t& count) noexcept { +#define NAMEOF_ENUM_V(O) \ + if constexpr ((I + O) < Size) { \ + if constexpr (is_valid(I + O)>()) { \ + valid[I + O] = true; \ + ++count; \ + } \ } - return count; + NAMEOF_FOR_EACH_256(NAMEOF_ENUM_V) + + if constexpr ((I + 256) < Size) { + valid_count(valid, count); + } +#undef NAMEOF_ENUM_V } -template -constexpr auto values(std::index_sequence) noexcept { - static_assert(is_enum_v, "nameof::detail::values requires enum type."); - constexpr bool valid[sizeof...(I)] = {is_valid(I)>()...}; - constexpr std::size_t count = values_count(valid); +template +struct valid_count_t { + std::size_t count = 0; + bool valid[N] = {}; +}; - if constexpr (count > 0) { - E values[count] = {}; - for (std::size_t i = 0, v = 0; v < count; ++i) { - if (valid[i]) { +template +constexpr auto valid_count() noexcept { + valid_count_t vc; + valid_count(vc.valid, vc.count); + return vc; +} + +template +constexpr auto values() noexcept { + constexpr auto vc = valid_count(); + + if constexpr (vc.count > 0) { +#if defined(NAMEOF_ARRAY_CONSTEXPR) + std::array values = {}; +#else + E values[vc.count] = {}; +#endif + for (std::size_t i = 0, v = 0; v < vc.count; ++i) { + if (vc.valid[i]) { values[v++] = value(i); } } - - return to_array(values, std::make_index_sequence{}); +#if defined(NAMEOF_ARRAY_CONSTEXPR) + return values; +#else + return to_array(values, std::make_index_sequence{}); +#endif } else { return std::array{}; } @@ -698,22 +720,18 @@ constexpr auto values(std::index_sequence) noexcept { template > constexpr auto values() noexcept { - static_assert(is_enum_v, "nameof::detail::values requires enum type."); - constexpr auto min = reflected_min_v; - constexpr auto max = reflected_max_v; + constexpr auto min = reflected_min(); + constexpr auto max = reflected_max(); constexpr auto range_size = max - min + 1; static_assert(range_size > 0, "nameof::enum_range requires valid size."); static_assert(range_size < (std::numeric_limits::max)(), "nameof::enum_range requires valid size."); - return values(std::make_index_sequence{}); + return values(); } template inline constexpr auto values_v = values(); -template > -using values_t = decltype((values_v)); - template inline constexpr auto count_v = values_v.size(); @@ -723,57 +741,35 @@ inline constexpr auto min_v = (count_v > 0) ? static_cast(values_ template > inline constexpr auto max_v = (count_v > 0) ? static_cast(values_v.back()) : U{0}; -template > -constexpr std::size_t range_size() noexcept { - static_assert(is_enum_v, "nameof::detail::range_size requires enum type."); - constexpr auto max = IsFlags ? log2(max_v) : max_v; - constexpr auto min = IsFlags ? log2(min_v) : min_v; - constexpr auto range_size = max - min + U{1}; - static_assert(range_size > 0, "nameof::enum_range requires valid size."); - static_assert(range_size < (std::numeric_limits::max)(), "nameof::enum_range requires valid size."); - - return static_cast(range_size); -} - -template -inline constexpr auto range_size_v = range_size(); - -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::index_sequence) noexcept { - static_assert(is_enum_v, "nameof::detail::indexes requires enum type."); - constexpr auto min = IsFlags ? log2(min_v) : min_v; - [[maybe_unused]] auto i = index_t{0}; - - return std::array{{(is_valid(I)>() ? i++ : invalid_index_v)...}}; +constexpr auto names(std::index_sequence) noexcept { + constexpr auto names = std::array{{enum_name_v[I]>...}}; + return names; } template -inline constexpr auto indexes_v = indexes(std::make_index_sequence>{}); +inline constexpr auto names_v = names(std::make_index_sequence>{}); template > constexpr bool is_sparse() noexcept { - static_assert(is_enum_v, "nameof::detail::is_sparse requires enum type."); - - if constexpr (IsFlags) { - return (sizeof(const char*) * range_size_v) > (sizeof(E) * count_v + sizeof(const char*) * count_v); + if constexpr (count_v == 0) { + return false; + } else if constexpr (std::is_same_v) { // bool special case + return false; } else { - return (sizeof(const char*) * range_size_v) > (sizeof(index_t) * range_size_v + sizeof(const char*) * count_v); + constexpr auto max = IsFlags ? log2(max_v) : max_v; + constexpr auto min = IsFlags ? log2(min_v) : min_v; + constexpr auto range_size = max - min + 1; + + return range_size != count_v; } } template inline constexpr bool is_sparse_v = is_sparse(); -template > -[[nodiscard]] constexpr E get_value(std::size_t i) noexcept { - static_assert(is_enum_v, "nameof::detail::strings requires enum type."); - +template > +constexpr E enum_value(std::size_t i) noexcept { if constexpr (is_sparse_v) { return values_v[i]; } else { @@ -783,24 +779,6 @@ template > } } -template -constexpr auto strings(std::index_sequence) noexcept { - static_assert(is_enum_v, "nameof::detail::strings requires enum type."); - - return std::array{{enum_name_v(I)>.data()...}}; -} - -template -constexpr auto strings() noexcept { - static_assert(is_enum_v, "nameof::detail::strings requires enum type."); - constexpr auto count = is_sparse_v ? count_v : range_size_v; - - return strings(std::make_index_sequence{}); -} - -template -inline static constexpr auto strings_v = strings(); - template struct nameof_type_supported #if defined(NAMEOF_TYPE_SUPPORTED) && NAMEOF_TYPE_SUPPORTED || defined(NAMEOF_TYPE_NO_CHECK_SUPPORT) @@ -1077,14 +1055,16 @@ template static_assert(detail::nameof_enum_supported::value, "nameof::nameof_enum unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); static_assert(detail::count_v > 0, "nameof::nameof_enum requires enum implementation and valid max and min."); - if (static_cast(value) >= static_cast(detail::min_v) && static_cast(value) <= static_cast(detail::max_v)) { - const auto i = static_cast(value) - detail::min_v; - if constexpr (detail::is_sparse_v) { - if (const auto idx = detail::indexes_v[i]; idx != detail::invalid_index_v) { - return detail::strings_v[idx]; + if constexpr (detail::is_sparse_v) { + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (detail::enum_value(i) == value) { + return detail::names_v[i]; } - } else { - return detail::strings_v[static_cast(i)]; + } + } else { + const auto v = static_cast(value); + if (v >= detail::min_v && v <= detail::max_v) { + return detail::names_v[static_cast(v - detail::min_v)]; } } return {}; // Value out of range. @@ -1110,18 +1090,17 @@ template using U = std::underlying_type_t; static_assert(detail::nameof_enum_supported::value, "nameof::nameof_enum_flag unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); static_assert(detail::count_v > 0, "nameof::nameof_enum_flag requires enum-flags implementation."); - constexpr auto size = detail::is_sparse_v ? detail::count_v : detail::range_size_v; string name; auto check_value = U{0}; - for (std::size_t i = 0; i < size; ++i) { - if (const auto v = static_cast(detail::get_value(i)); (static_cast(value) & v) != 0) { - if (const auto n = detail::strings_v[i]; n != nullptr) { + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (const auto v = static_cast(detail::enum_value(i)); (static_cast(value) & v) != 0) { + if (const auto n = detail::names_v[i]; !n.empty()) { check_value |= v; if (!name.empty()) { name.append(1, sep); } - name.append(n); + name.append(n.data(), n.size()); } else { return {}; // Value out of range. } diff --git a/test/test_aliases.cpp b/test/test_aliases.cpp index f41fcb3..df82ca9 100644 --- a/test/test_aliases.cpp +++ b/test/test_aliases.cpp @@ -32,7 +32,7 @@ struct MyString { MyString(const char* s, std::size_t l) : str{s, l} {} // required bool empty() const { return str.empty(); } // required MyString& append(std::size_t count, char c) { str.append(count, c); return *this; } // required - MyString& append(const char* s) { str.append(s); return *this; } // required + MyString& append(const char* s, std::size_t count) { str.append(s, count); return *this; } // required MyString& append(const MyString& s) { str.append(s.str); return *this; } // required std::size_t size() const { return str.size(); }