From 1f071407dcc742650ffdfec10d19d64f647baf33 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Fri, 17 Mar 2023 17:39:30 +0000 Subject: [PATCH] Nameof pointer + tests (#59) --- include/nameof.hpp | 82 ++++++++++++++++++++++++++++++++++++++++++---- test/test.cpp | 36 ++++++++++++++++++++ 2 files changed, 112 insertions(+), 6 deletions(-) diff --git a/include/nameof.hpp b/include/nameof.hpp index f22cfdd..565d667 100644 --- a/include/nameof.hpp +++ b/include/nameof.hpp @@ -103,6 +103,12 @@ # define NAMEOF_MEMBER_SUPPORTED 1 #endif +// Checks nameof_pointer compiler compatibility. +#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 7 || defined(_MSC_VER) && defined(_MSVC_LANG) && _MSVC_LANG >= 202002L +# undef NAMEOF_POINTER_SUPPORTED +# define NAMEOF_POINTER_SUPPORTED 1 +#endif + // Checks nameof_enum compiler compatibility. #if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 9 || defined(_MSC_VER) && _MSC_VER >= 1910 # undef NAMEOF_ENUM_SUPPORTED @@ -146,8 +152,8 @@ using std::string; namespace customize { // Enum value must be in range [NAMEOF_ENUM_RANGE_MIN, NAMEOF_ENUM_RANGE_MAX]. By default NAMEOF_ENUM_RANGE_MIN = -128, NAMEOF_ENUM_RANGE_MAX = 128. -// If need another range for all enum types by default, redefine the macro NAMEOF_ENUM_RANGE_MIN and NAMEOF_ENUM_RANGE_MAX. -// If need another range for specific enum type, add specialization enum_range for necessary enum type. +// If you need another range for all enum types by default, redefine the macro NAMEOF_ENUM_RANGE_MIN and NAMEOF_ENUM_RANGE_MAX. +// If you need another range for specific enum type, add specialization enum_range for necessary enum type. template struct enum_range { static_assert(std::is_enum_v, "nameof::customize::enum_range requires enum type."); @@ -164,7 +170,7 @@ static_assert(NAMEOF_ENUM_RANGE_MAX < (std::numeric_limits::max)() static_assert(NAMEOF_ENUM_RANGE_MAX > NAMEOF_ENUM_RANGE_MIN, "NAMEOF_ENUM_RANGE_MAX must be greater than NAMEOF_ENUM_RANGE_MIN."); -// If need custom names for enum, add specialization enum_name for necessary enum type. +// If you need custom names for enum, add specialization enum_name for necessary enum type. template constexpr string_view enum_name(E) noexcept { static_assert(std::is_enum_v, "nameof::customize::enum_name requires enum type."); @@ -172,18 +178,24 @@ constexpr string_view enum_name(E) noexcept { return {}; } -// If need custom name for type, add specialization type_name for necessary type. +// If you need custom name for type, add specialization type_name for necessary type. template constexpr string_view type_name() noexcept { return {}; } -// If need custom name for member, add specialization member_name for necessary type. +// If you need custom name for member, add specialization member_name for necessary type. template constexpr string_view member_name() noexcept { return {}; } +// If you need custom name for a pointer, add specialization pointer_name for necessary type. +template +constexpr string_view pointer_name() noexcept { + return {}; +} + } // namespace nameof::customize template @@ -815,6 +827,14 @@ struct nameof_member_supported : std::false_type {}; #endif +template +struct nameof_pointer_supported +#if defined(NAMEOF_POINTER_SUPPORTED) && NAMEOF_POINTER_SUPPORTED || defined(NAMEOF_TYPE_NO_CHECK_SUPPORT) + : std::true_type {}; +#else + : std::false_type {}; +#endif + #if defined(_MSC_VER) && !defined(__clang__) template struct identity { @@ -1005,6 +1025,40 @@ template inline constexpr auto member_name_v = cstring<0>{}; #endif +template +struct is_same : std::false_type {}; + +template +struct is_same : std::true_type {}; + +template +constexpr bool is_nullptr_v = is_same>(nullptr)>::value; + +template +constexpr auto p() noexcept { + [[maybe_unused]] constexpr auto custom_name = + customize::pointer_name().empty() && is_nullptr_v ? "nullptr" : customize::pointer_name(); + + if constexpr (custom_name.empty() && nameof_pointer_supported::value) { +#if defined(__clang__) + constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); +#elif defined(__GNUC__) + constexpr bool has_parenthesis = __PRETTY_FUNCTION__[sizeof(__PRETTY_FUNCTION__) - 3] == ')'; + constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2 - has_parenthesis}); +#elif defined(_MSC_VER) && defined(_MSVC_LANG) && _MSVC_LANG >= 202002L + constexpr auto name = pretty_name({__FUNCSIG__, sizeof(__FUNCSIG__) - 17}); +#else + constexpr auto name = string_view{}; +#endif + return cstring{name}; + } else { + return cstring{custom_name}; + } +} + +template +inline constexpr auto pointer_name_v = p(); + } // namespace nameof::detail // Checks is nameof_type supported compiler. @@ -1016,6 +1070,9 @@ inline constexpr bool is_nameof_type_rtti_supported = detail::nameof_type_rtti_s // Checks is nameof_member supported compiler. inline constexpr bool is_nameof_member_supported = detail::nameof_member_supported::value; +// Checks is nameof_pointer supported compiler. +inline constexpr bool is_nameof_pointer_supported = detail::nameof_pointer_supported::value; + // Checks is nameof_enum supported compiler. inline constexpr bool is_nameof_enum_supported = detail::nameof_enum_supported::value; @@ -1134,13 +1191,23 @@ template // Obtains name of member. template [[nodiscard]] constexpr auto nameof_member() noexcept -> std::enable_if_t, string_view> { - static_assert(detail::nameof_member_supported::value, "nameof::nameof_memder unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + static_assert(detail::nameof_member_supported::value, "nameof::nameof_member unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); constexpr string_view name = detail::member_name_v; static_assert(!name.empty(), "Member does not have a name."); return name; } +// Obtains name of a function, a global or class static variable. +template +[[nodiscard]] constexpr auto nameof_pointer() noexcept -> std::enable_if_t, string_view> { + static_assert(detail::nameof_pointer_supported::value, "nameof::nameof_pointer unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + constexpr string_view name = detail::pointer_name_v; + static_assert(!name.empty(), "Pointer does not have a name."); + + return name; +} + } // namespace nameof // Obtains name of variable, function, macro. @@ -1213,6 +1280,9 @@ template // Obtains name of member. #define NAMEOF_MEMBER(...) ::nameof::nameof_member<__VA_ARGS__>() +// Obtains name of a function, a global or class static variable. +#define NAMEOF_POINTER(...) ::nameof::nameof_pointer<__VA_ARGS__>() + #if defined(__clang__) # pragma clang diagnostic pop #elif defined(__GNUC__) diff --git a/test/test.cpp b/test/test.cpp index d31d640..24b3727 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -46,8 +46,16 @@ struct SomeStruct { int SomeMethod2() const { throw std::runtime_error{"should not be called!"}; } + + static int somestaticfield; + constexpr static int someotherstaticfield = 21; }; +int SomeStruct::somestaticfield; + +int someglobalvariable = 0; +const int someglobalconstvariable = 42; + void SomeMethod3() { throw std::runtime_error{"should not be called!"}; } @@ -940,3 +948,31 @@ TEST_CASE("nameof_member") { } #endif + +#if defined(NAMEOF_POINTER_SUPPORTED) && NAMEOF_POINTER_SUPPORTED + +void somefunction() {} + +TEST_CASE("NAMEOF_POINTER") { + REQUIRE(NAMEOF_POINTER(&SomeStruct::somestaticfield) == "somestaticfield"); + REQUIRE(NAMEOF_POINTER(&SomeStruct::someotherstaticfield) == "someotherstaticfield"); + REQUIRE(NAMEOF_POINTER(static_cast(nullptr)) == "nullptr"); + REQUIRE(NAMEOF_POINTER(static_cast(nullptr)) == "nullptr"); + constexpr auto global_ptr = &someglobalvariable; + REQUIRE(NAMEOF_POINTER(global_ptr) == "someglobalvariable"); + REQUIRE(NAMEOF_POINTER(&someglobalconstvariable) == "someglobalconstvariable"); + REQUIRE(NAMEOF_POINTER(&somefunction) == "somefunction"); +} + +TEST_CASE("nameof_pointer") { + REQUIRE(nameof::nameof_pointer<&SomeStruct::somestaticfield>() == "somestaticfield"); + REQUIRE(nameof::nameof_pointer<&SomeStruct::someotherstaticfield>() == "someotherstaticfield"); + REQUIRE(nameof::nameof_pointer(nullptr)>() == "nullptr"); + REQUIRE(nameof::nameof_pointer(nullptr)>() == "nullptr"); + constexpr auto global_ptr = &someglobalvariable; + REQUIRE(nameof::nameof_pointer() == "someglobalvariable"); + REQUIRE(nameof::nameof_pointer<&someglobalconstvariable>() == "someglobalconstvariable"); + REQUIRE(nameof::nameof_pointer<&somefunction>() == "somefunction"); +} + +#endif \ No newline at end of file