diff --git a/README.md b/README.md index 7b234a7..7885077 100644 --- a/README.md +++ b/README.md @@ -86,10 +86,19 @@ Header-only C++17 library provides nameof macros and functions to obtain simple * Nameof expression argument are identified, but do not evaluated. -* Enum variable must be in range `(-NAMEOF_ENUM_RANGE, NAMEOF_ENUM_RANGE)`. By default `NAMEOF_ENUM_RANGE = 128`. If you need a larger range, redefine the macro `NAMEOF_ENUM_RANGE`. +* Enum value must be in range `[-256, 256]`. If you need another range, add specialization enum_range for necessary enum type. ```cpp - #define NAMEOF_ENUM_RANGE 1028 // Redefine NAMEOF_ENUM_RANGE for larger range. #include + + enum number { one = 100, two = 200, three = 300 }; + + namespace nameof { + template <> + struct enum_range { + static constexpr int min = 100; + static constexpr int max = 300; + }; + } ``` * If you need name with template suffix, use NAMEOF_FULL. @@ -113,6 +122,45 @@ Header-only C++17 library provides nameof macros and functions to obtain simple NAMEOF( somevar ) -> "somevar" ``` +* Nameof enum obtains the first defined value enums, and won't work if value are aliased. + ```cpp + enum ShapeKind { + ConvexBegin = 0, + Box = 0, // Won't work. + Sphere = 1, + ConvexEnd = 2, + Donut = 2, // Won't work too. + Banana = 3, + COUNT = 4, + }; + // NAMEOF_ENUM(ShapeKind::Box) -> "ConvexBegin" + // nameof::nameof_enum(ShapeKind::Box) -> "ConvexBegin" + ``` + Work around the issue: + ```cpp + enum ShapeKind { + // Convex shapes, see ConvexBegin and ConvexEnd below. + Box = 0, + Sphere = 1, + + // Non-convex shapes. + Donut = 2, + Banana = 3, + + COUNT = Banana + 1, + + // Non-reflected aliases. + ConvexBegin = Box, + ConvexEnd = Sphere + 1, + }; + // NAMEOF_ENUM(ShapeKind::Box) -> "Box" + // nameof::nameof_enum(ShapeKind::Box) -> "Box" + + // Non-reflected aliases. + // NAMEOF_ENUM(ShapeKind::ConvexBegin) -> "Box" + // nameof::nameof_enum(ShapeKind::ConvexBegin) -> "Box" + ``` + ## Integration You should add required file [nameof.hpp](include/nameof.hpp). diff --git a/example/example.cpp b/example/example.cpp index 399e2ac..0a3154e 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -93,11 +93,6 @@ int main() { std::cout << nameof::nameof_enum(color) << std::endl; // 'RED' std::cout << NAMEOF_ENUM(color) << std::endl; // 'RED' - // Nameof static storage enum variable. - constexpr auto const_color = Color::BLUE; - std::cout << nameof::nameof_enum() << std::endl; // 'BLUE' - std::cout << NAMEOF_CONST_ENUM(const_color) << std::endl; // 'BLUE' - // Nameof. std::cout << NAMEOF(structvar) << std::endl; // 'structvar' std::cout << NAMEOF(::structvar) << std::endl; // 'structvar' diff --git a/include/nameof.hpp b/include/nameof.hpp index 80bb121..abafc76 100644 --- a/include/nameof.hpp +++ b/include/nameof.hpp @@ -31,24 +31,21 @@ #pragma once +#include #include -#include #include +#include #include -// Enum variable must be in range (-NAMEOF_ENUM_RANGE, NAMEOF_ENUM_RANGE). If you need a larger range, redefine the macro NAMEOF_ENUM_RANGE. -#if !defined(NAMEOF_ENUM_RANGE) -# define NAMEOF_ENUM_RANGE 128 -#endif - namespace nameof { -static_assert(NAMEOF_ENUM_RANGE > 0, - "NAMEOF_ENUM_RANGE must be positive and greater than zero."); -static_assert(NAMEOF_ENUM_RANGE % 8 == 0, - "NAMEOF_ENUM_RANGE must be a multiple of 8."); -static_assert(NAMEOF_ENUM_RANGE < std::numeric_limits::max(), - "NAMEOF_ENUM_RANGE must be less INT_MAX."); +// Enum value must be in range [-256, 256]. If you need another range, add specialization enum_range for necessary enum type. +template +struct enum_range final { + static_assert(std::is_enum_v, "nameof::enum_range requires enum type."); + static constexpr int min = std::is_signed_v> ? -256 : 0; + static constexpr int max = 256; +}; namespace detail { @@ -168,13 +165,14 @@ template while (name.back() == ' ') { name.remove_suffix(1); } + return name; #endif } template -[[nodiscard]] constexpr std::string_view nameof_enum_impl() noexcept { - static_assert(std::is_enum_v, "nameof::nameof_enum require enum type."); +[[nodiscard]] constexpr std::string_view enum_name_impl() noexcept { + static_assert(std::is_enum_v, "nameof::enum_name_impl requires enum type."); #if defined(__clang__) std::string_view name{__PRETTY_FUNCTION__}; constexpr auto suffix = sizeof("]") - 1; @@ -190,48 +188,37 @@ template #if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 9) || defined(_MSC_VER) name.remove_suffix(suffix); + return pretty_name(name, false); #endif } -template -struct nameof_enum_impl_t final { - [[nodiscard]] constexpr std::string_view operator()(int value) const noexcept { - static_assert(std::is_enum_v, "nameof::nameof_enum require enum type."); - if constexpr (V > std::numeric_limits>::max()) { - return {}; // Enum variable out of range. - } +template +[[nodiscard]] constexpr decltype(auto) enum_strings_impl(std::integer_sequence) noexcept { + static_assert(std::is_enum_v, "magic_enum::detail::enum_strings_impl requires enum type."); + using U = std::underlying_type_t; + constexpr int min = (enum_range::min > std::numeric_limits::min()) ? enum_range::min : std::numeric_limits::min(); + constexpr std::array enum_names{{enum_name_impl(I + min)>()...}}; - switch (value - V) { - case 0: - return nameof_enum_impl(V)>(); - case 1: - return nameof_enum_impl(V + 1)>(); - case 2: - return nameof_enum_impl(V + 2)>(); - case 3: - return nameof_enum_impl(V + 3)>(); - case 4: - return nameof_enum_impl(V + 4)>(); - case 5: - return nameof_enum_impl(V + 5)>(); - case 6: - return nameof_enum_impl(V + 6)>(); - case 7: - return nameof_enum_impl(V + 7)>(); - default: - return nameof_enum_impl_t{}(value); - } - } -}; + return enum_names; +} template -struct nameof_enum_impl_t final { - [[nodiscard]] constexpr std::string_view operator()(int) const noexcept { - static_assert(std::is_enum_v, "nameof::nameof_enum require enum type."); - return {}; // Enum variable out of range NAMEOF_ENUM_RANGE. +[[nodiscard]] constexpr std::string_view nameof_enum_impl(int value) noexcept { + static_assert(std::is_enum_v, "magic_enum::detail::nameof_enum_impl requires enum type."); + using U = std::underlying_type_t; + constexpr int max = (enum_range::max < std::numeric_limits::max()) ? enum_range::max : std::numeric_limits::max(); + constexpr int min = (enum_range::min > std::numeric_limits::min()) ? enum_range::min : std::numeric_limits::min(); + constexpr auto enum_range = std::make_integer_sequence{}; + constexpr auto enum_names = enum_strings_impl(enum_range); + const int i = value - min; + + if (i >= 0 && static_cast(i) < enum_names.size()) { + return enum_names[i]; + } else { + return {}; } -}; +} template [[nodiscard]] constexpr std::string_view nameof_impl(std::string_view name, bool with_template_suffix) noexcept { @@ -248,18 +235,7 @@ template // nameof_enum(enum) obtains simple (unqualified) string enum name of enum variable. template >>> [[nodiscard]] constexpr std::string_view nameof_enum(T value) noexcept { - constexpr bool s = std::is_signed_v>>; - constexpr int min = s ? -NAMEOF_ENUM_RANGE : 0; - if (static_cast(value) >= NAMEOF_ENUM_RANGE || static_cast(value) <= -NAMEOF_ENUM_RANGE) { - return {}; // Enum variable out of range NAMEOF_ENUM_RANGE. - } - return detail::nameof_enum_impl_t, min>{}(static_cast(value)); -} - -// nameof_enum() obtains simple (unqualified) string enum name of static storage enum variable. -template >>> -[[nodiscard]] constexpr std::string_view nameof_enum() noexcept { - return detail::nameof_enum_impl(); + return detail::nameof_enum_impl>(static_cast(value)); } // nameof_type() obtains string name of type. @@ -282,9 +258,6 @@ template // NAMEOF_ENUM obtains simple (unqualified) string enum name of enum variable. #define NAMEOF_ENUM(...) ::nameof::nameof_enum(__VA_ARGS__) -// NAMEOF_CONST_ENUM obtains simple (unqualified) string enum name of static storage enum variable. -#define NAMEOF_CONST_ENUM(...) ::nameof::nameof_enum<__VA_ARGS__>() - // NAMEOF_TYPE obtains string name of type. #define NAMEOF_TYPE(...) ::nameof::nameof_type<__VA_ARGS__>() diff --git a/test/test.cpp b/test/test.cpp index 3378715..4bc308a 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -23,7 +23,6 @@ #define CATCH_CONFIG_MAIN #include -#define NAMEOF_ENUM_RANGE 120 #include #include @@ -205,11 +204,14 @@ TEST_CASE("NAMEOF_RAW") { TEST_CASE("NAMEOF_ENUM") { #if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 9) || defined(_MSC_VER) - Color color_ = Color::BLUE; + constexpr Color color_ = Color::BLUE; Color m[3] = {Color::RED, Color::GREEN, Color::BLUE}; + REQUIRE(NAMEOF_ENUM(Color::RED) == "RED"); REQUIRE(NAMEOF_ENUM(color) == "RED"); + constexpr auto color_name = NAMEOF_ENUM(color_); + REQUIRE(color_name == "BLUE"); REQUIRE(NAMEOF_ENUM(Color::BLUE) == "BLUE"); REQUIRE(NAMEOF_ENUM(color_) == "BLUE"); @@ -218,10 +220,8 @@ TEST_CASE("NAMEOF_ENUM") { REQUIRE(NAMEOF_ENUM(Directions::Right) == "Right"); REQUIRE(NAMEOF_ENUM(directions) == "Right"); - REQUIRE(NAMEOF_ENUM((Color)NAMEOF_ENUM_RANGE).empty()); REQUIRE(NAMEOF_ENUM((Color)-100).empty()); REQUIRE(NAMEOF_ENUM((Color)100).empty()); - REQUIRE(NAMEOF_ENUM((Directions)NAMEOF_ENUM_RANGE).empty()); REQUIRE(NAMEOF_ENUM((Directions)100).empty()); REQUIRE(NAMEOF_ENUM((Directions)100).empty()); #endif @@ -229,11 +229,14 @@ TEST_CASE("NAMEOF_ENUM") { TEST_CASE("nameof::nameof_enum(enum)") { #if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 9) || defined(_MSC_VER) - Color color_ = Color::BLUE; + constexpr Color color_ = Color::BLUE; Color m[3] = {Color::RED, Color::GREEN, Color::BLUE}; + REQUIRE(nameof::nameof_enum(Color::RED) == "RED"); REQUIRE(nameof::nameof_enum(color) == "RED"); + constexpr auto color_name = nameof::nameof_enum(color_); + REQUIRE(color_name == "BLUE"); REQUIRE(nameof::nameof_enum(Color::BLUE) == "BLUE"); REQUIRE(nameof::nameof_enum(color_) == "BLUE"); @@ -242,63 +245,13 @@ TEST_CASE("nameof::nameof_enum(enum)") { REQUIRE(nameof::nameof_enum(Directions::Right) == "Right"); REQUIRE(nameof::nameof_enum(directions) == "Right"); - REQUIRE(nameof::nameof_enum((Color)NAMEOF_ENUM_RANGE).empty()); REQUIRE(nameof::nameof_enum((Color)-100).empty()); REQUIRE(nameof::nameof_enum((Color)100).empty()); - REQUIRE(nameof::nameof_enum((Directions)NAMEOF_ENUM_RANGE).empty()); REQUIRE(nameof::nameof_enum((Directions)100).empty()); REQUIRE(nameof::nameof_enum((Directions)100).empty()); #endif } -TEST_CASE("NAMEOF_CONST_ENUM") { -#if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 9) || defined(_MSC_VER) - static const Color color_ = Color::BLUE; - constexpr Color m[3] = {Color::RED, Color::GREEN, Color::BLUE}; - REQUIRE(NAMEOF_CONST_ENUM(Color::RED) == "RED"); - REQUIRE(NAMEOF_CONST_ENUM(color) == "RED"); - - REQUIRE(NAMEOF_CONST_ENUM(Color::BLUE) == "BLUE"); - REQUIRE(NAMEOF_CONST_ENUM(color_) == "BLUE"); - - REQUIRE(NAMEOF_CONST_ENUM(m[1]) == "GREEN"); - - REQUIRE(NAMEOF_CONST_ENUM(Directions::Right) == "Right"); - REQUIRE(NAMEOF_CONST_ENUM(directions) == "Right"); - - REQUIRE(NAMEOF_CONST_ENUM((Color)NAMEOF_ENUM_RANGE).empty()); - REQUIRE(NAMEOF_CONST_ENUM((Color)-100).empty()); - REQUIRE(NAMEOF_CONST_ENUM((Color)100).empty()); - REQUIRE(NAMEOF_CONST_ENUM((Directions)NAMEOF_ENUM_RANGE).empty()); - REQUIRE(NAMEOF_CONST_ENUM((Directions)100).empty()); - REQUIRE(NAMEOF_CONST_ENUM((Directions)100).empty()); -#endif -} - -TEST_CASE("nameof::nameof_enum()") { -#if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 9) || defined(_MSC_VER) - static const Color color_ = Color::BLUE; - constexpr Color m[3] = {Color::RED, Color::GREEN, Color::BLUE}; - REQUIRE(nameof::nameof_enum() == "RED"); - REQUIRE(nameof::nameof_enum() == "RED"); - - REQUIRE(nameof::nameof_enum() == "BLUE"); - REQUIRE(nameof::nameof_enum() == "BLUE"); - - REQUIRE(nameof::nameof_enum() == "GREEN"); - - REQUIRE(nameof::nameof_enum() == "Right"); - REQUIRE(nameof::nameof_enum() == "Right"); - - REQUIRE(nameof::nameof_enum<(Color)NAMEOF_ENUM_RANGE>().empty()); - REQUIRE(nameof::nameof_enum<(Color)-100>().empty()); - REQUIRE(nameof::nameof_enum<(Color)100>().empty()); - REQUIRE(nameof::nameof_enum<(Directions)NAMEOF_ENUM_RANGE>().empty()); - REQUIRE(nameof::nameof_enum<(Directions)100>().empty()); - REQUIRE(nameof::nameof_enum<(Directions)100>().empty()); -#endif -} - TEST_CASE("NAMEOF_VAR_TYPE") { #if defined(__clang__) REQUIRE(NAMEOF_VAR_TYPE(struct_var) == "SomeStruct");