Nameof pointer + tests (#59)

This commit is contained in:
Bela Schaum 2023-03-17 17:39:30 +00:00 committed by GitHub
parent 9494cbd4aa
commit 1f071407dc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 112 additions and 6 deletions

View file

@ -103,6 +103,12 @@
# define NAMEOF_MEMBER_SUPPORTED 1 # define NAMEOF_MEMBER_SUPPORTED 1
#endif #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. // Checks nameof_enum compiler compatibility.
#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 9 || defined(_MSC_VER) && _MSC_VER >= 1910 #if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 9 || defined(_MSC_VER) && _MSC_VER >= 1910
# undef NAMEOF_ENUM_SUPPORTED # undef NAMEOF_ENUM_SUPPORTED
@ -146,8 +152,8 @@ using std::string;
namespace customize { 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. // 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 you 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 specific enum type, add specialization enum_range for necessary enum type.
template <typename E> template <typename E>
struct enum_range { struct enum_range {
static_assert(std::is_enum_v<E>, "nameof::customize::enum_range requires enum type."); static_assert(std::is_enum_v<E>, "nameof::customize::enum_range requires enum type.");
@ -164,7 +170,7 @@ static_assert(NAMEOF_ENUM_RANGE_MAX < (std::numeric_limits<std::int16_t>::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.");
// 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 <typename E> template <typename E>
constexpr string_view enum_name(E) noexcept { constexpr string_view enum_name(E) noexcept {
static_assert(std::is_enum_v<E>, "nameof::customize::enum_name requires enum type."); static_assert(std::is_enum_v<E>, "nameof::customize::enum_name requires enum type.");
@ -172,18 +178,24 @@ constexpr string_view enum_name(E) noexcept {
return {}; 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 <typename T> template <typename T>
constexpr string_view type_name() noexcept { constexpr string_view type_name() noexcept {
return {}; 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 <auto V> template <auto V>
constexpr string_view member_name() noexcept { constexpr string_view member_name() noexcept {
return {}; return {};
} }
// If you need custom name for a pointer, add specialization pointer_name for necessary type.
template <auto V>
constexpr string_view pointer_name() noexcept {
return {};
}
} // namespace nameof::customize } // namespace nameof::customize
template <std::uint16_t N> template <std::uint16_t N>
@ -815,6 +827,14 @@ struct nameof_member_supported
: std::false_type {}; : std::false_type {};
#endif #endif
template <typename... T>
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__) #if defined(_MSC_VER) && !defined(__clang__)
template <typename T> template <typename T>
struct identity { struct identity {
@ -1005,6 +1025,40 @@ template <auto V>
inline constexpr auto member_name_v = cstring<0>{}; inline constexpr auto member_name_v = cstring<0>{};
#endif #endif
template<auto U, auto V>
struct is_same : std::false_type {};
template<auto U>
struct is_same<U, U> : std::true_type {};
template<auto P>
constexpr bool is_nullptr_v = is_same<P, static_cast<std::remove_reference_t<decltype(P)>>(nullptr)>::value;
template <auto V>
constexpr auto p() noexcept {
[[maybe_unused]] constexpr auto custom_name =
customize::pointer_name<V>().empty() && is_nullptr_v<V> ? "nullptr" : customize::pointer_name<V>();
if constexpr (custom_name.empty() && nameof_pointer_supported<decltype(V)>::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.size()>{name};
} else {
return cstring<custom_name.size()>{custom_name};
}
}
template <auto V>
inline constexpr auto pointer_name_v = p<V>();
} // namespace nameof::detail } // namespace nameof::detail
// Checks is nameof_type supported compiler. // 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. // Checks is nameof_member supported compiler.
inline constexpr bool is_nameof_member_supported = detail::nameof_member_supported<void>::value; inline constexpr bool is_nameof_member_supported = detail::nameof_member_supported<void>::value;
// Checks is nameof_pointer supported compiler.
inline constexpr bool is_nameof_pointer_supported = detail::nameof_pointer_supported<void>::value;
// Checks is nameof_enum supported compiler. // Checks is nameof_enum supported compiler.
inline constexpr bool is_nameof_enum_supported = detail::nameof_enum_supported<void>::value; inline constexpr bool is_nameof_enum_supported = detail::nameof_enum_supported<void>::value;
@ -1134,13 +1191,23 @@ template <typename T>
// Obtains name of member. // Obtains name of member.
template <auto V> template <auto V>
[[nodiscard]] constexpr auto nameof_member() noexcept -> std::enable_if_t<std::is_member_pointer_v<decltype(V)>, string_view> { [[nodiscard]] constexpr auto nameof_member() noexcept -> std::enable_if_t<std::is_member_pointer_v<decltype(V)>, string_view> {
static_assert(detail::nameof_member_supported<decltype(V)>::value, "nameof::nameof_memder unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); static_assert(detail::nameof_member_supported<decltype(V)>::value, "nameof::nameof_member unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility).");
constexpr string_view name = detail::member_name_v<V>; constexpr string_view name = detail::member_name_v<V>;
static_assert(!name.empty(), "Member does not have a name."); static_assert(!name.empty(), "Member does not have a name.");
return name; return name;
} }
// Obtains name of a function, a global or class static variable.
template <auto V>
[[nodiscard]] constexpr auto nameof_pointer() noexcept -> std::enable_if_t<std::is_pointer_v<decltype(V)>, string_view> {
static_assert(detail::nameof_pointer_supported<decltype(V)>::value, "nameof::nameof_pointer unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility).");
constexpr string_view name = detail::pointer_name_v<V>;
static_assert(!name.empty(), "Pointer does not have a name.");
return name;
}
} // namespace nameof } // namespace nameof
// Obtains name of variable, function, macro. // Obtains name of variable, function, macro.
@ -1213,6 +1280,9 @@ template <auto V>
// Obtains name of member. // Obtains name of member.
#define NAMEOF_MEMBER(...) ::nameof::nameof_member<__VA_ARGS__>() #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__) #if defined(__clang__)
# pragma clang diagnostic pop # pragma clang diagnostic pop
#elif defined(__GNUC__) #elif defined(__GNUC__)

View file

@ -46,8 +46,16 @@ struct SomeStruct {
int SomeMethod2() const { int SomeMethod2() const {
throw std::runtime_error{"should not be called!"}; 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() { void SomeMethod3() {
throw std::runtime_error{"should not be called!"}; throw std::runtime_error{"should not be called!"};
} }
@ -940,3 +948,31 @@ TEST_CASE("nameof_member") {
} }
#endif #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<const char*>(nullptr)) == "nullptr");
REQUIRE(NAMEOF_POINTER(static_cast<int***>(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<static_cast<const char*>(nullptr)>() == "nullptr");
REQUIRE(nameof::nameof_pointer<static_cast<int***>(nullptr)>() == "nullptr");
constexpr auto global_ptr = &someglobalvariable;
REQUIRE(nameof::nameof_pointer<global_ptr>() == "someglobalvariable");
REQUIRE(nameof::nameof_pointer<&someglobalconstvariable>() == "someglobalconstvariable");
REQUIRE(nameof::nameof_pointer<&somefunction>() == "somefunction");
}
#endif