diff --git a/example/example.cpp b/example/example.cpp index 84e0d12..576b611 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -56,10 +56,16 @@ T SomeMethod4() { template class SomeClass { public: - void SomeMethod5() const {} + void SomeMethod5() const { + std::cout << nameof::NameofType(false) << std::endl; + } template - C SomeMethod6() const { return C{}; } + C SomeMethod6() const { + C t{}; + std::cout << NAMEOF_TYPE(t) << std::endl; + return t; + } }; struct Long { @@ -77,24 +83,25 @@ SomeStruct& refvar = somevar; SomeStruct* ptrvar = &somevar; int main() { - std::cout << typeid(std::string).name() << std::endl; - // constexpr +#if __cplusplus >= 201402L || (defined(_MSVC_LANG ) && _MSVC_LANG >= 201402L ) + // Compile-time supported by C++14. constexpr auto constexpr_work_fine = NAMEOF(somevar); std::cout << constexpr_work_fine << std::endl; // somevar +#endif - // enum + // Enum name. std::cout << NAMEOF(Color::RED) << std::endl; // RED - // variable + // Variable name. std::cout << NAMEOF(somevar) << std::endl; // somevar std::cout << NAMEOF(::somevar) << std::endl; // somevar - // member + // Member name. std::cout << NAMEOF(somevar.somefield) << std::endl; // somefield std::cout << NAMEOF((&somevar)->somefield) << std::endl; // somefield std::cout << NAMEOF(othervar.ll.field) << std::endl; // field - // function + // Function name. std::cout << NAMEOF(&SomeStruct::SomeMethod1) << std::endl; // SomeMethod1 std::cout << NAMEOF(&SomeStruct::SomeMethod2) << std::endl; // SomeMethod2 std::cout << NAMEOF(SomeMethod3) << std::endl; // SomeMethod3 @@ -102,29 +109,25 @@ int main() { std::cout << NAMEOF(&SomeClass::SomeMethod5) << std::endl; // SomeMethod5 std::cout << NAMEOF(&SomeClass::SomeMethod6) << std::endl; // SomeMethod6 - // function with template prefix - std::cout << NAMEOF_T(SomeMethod4) << std::endl; // SomeMethod4 - std::cout << NAMEOF_T(&SomeClass::SomeMethod6) << std::endl; // SomeMethod6 - - // type - std::cout << NAMEOF_TYPE(std::string{}) << std::endl; // basic_string + // Type name. std::cout << NAMEOF_TYPE(somevar) << std::endl; // SomeStruct - std::cout << NAMEOF_TYPE(refvar) << std::endl; // SomeStruct& - std::cout << NAMEOF_TYPE(ptrvar) << std::endl; // SomeStruct* + std::cout << NAMEOF_TYPE(refvar) << std::endl; // SomeStruct + std::cout << NAMEOF_TYPE(ptrvar) << std::endl; // SomeStruct + std::cout << NAMEOF_TYPE(othervar.ll) << std::endl; // LL + std::cout << NAMEOF_TYPE(othervar.ll.field) << std::endl; // int std::cout << NAMEOF_TYPE(Color::RED) << std::endl; // Color - std::cout << NAMEOF_RAW(int[]) << std::endl; // int[] - std::cout << NAMEOF_RAW(SomeStruct) << std::endl; // SomeStruct - std::cout << NAMEOF_RAW(Long::LL) << std::endl; // Long::LL + std::cout << NAMEOF_TYPE(SomeClass{}) << std::endl; // SomeClass + + // Type full name. + std::cout << NAMEOF_TYPE_RAW(othervar.ll) << std::endl; // Long::LL + std::cout << NAMEOF_TYPE_RAW(std::declval>()) << std::endl; // SomeClass&& + + // Raw name. std::cout << NAMEOF_RAW(volatile const int) << std::endl; // volatile const int - - // macros std::cout << NAMEOF_RAW(__LINE__) << std::endl; // __LINE__ - std::cout << NAMEOF_RAW(__FILE__) << std::endl; // __FILE__ - - // full name std::cout << NAMEOF_RAW(somevar.somefield) << std::endl; // somevar.somefield - std::cout << NAMEOF_RAW(&SomeClass::SomeMethod6) << std::endl; // &SomeClass::SomeMethod6 + std::cout << NAMEOF_RAW(&SomeStruct::SomeMethod1) << std::endl; // &SomeStruct::SomeMethod1 std::cout << NAMEOF_RAW(Long::LL) << std::endl; // Long::LL const auto div = [](int x, int y) -> int { @@ -143,7 +146,7 @@ int main() { /* Remarks */ - // Spaces and Tabs ignored + // Spaces and Tabs ignored. std::cout << NAMEOF( somevar ) << std::endl; // somevar std::cout << NAMEOF( somevar ) << std::endl; // somevar diff --git a/include/nameof.hpp b/include/nameof.hpp index 8068bce..2ad2516 100644 --- a/include/nameof.hpp +++ b/include/nameof.hpp @@ -48,7 +48,7 @@ template struct remove_all_pointers : std::conditional::value, remove_all_pointers::type>, - identity> {}; + identity>::type {}; class cstring final { const char* str_; std::size_t size_; @@ -89,6 +89,25 @@ class cstring final { inline constexpr const char& operator[](std::size_t i) const { return str_[i]; } + inline constexpr const char& front() const { return str_[0]; } + + inline constexpr const char& back() const { return str_[size_ - 1]; } + + inline constexpr const char* data() const noexcept { return str_; } + + inline constexpr void remove_prefix(std::size_t n) { + str_ += n; + size_ -= n; + } + + inline constexpr void remove_suffix(std::size_t n) { + size_ -= n; + } + + inline constexpr cstring substr(std::size_t pos, std::size_t n) const { + return {str_ + pos, n}; + } + inline friend constexpr bool operator==(const cstring& lhs, const cstring& rhs) noexcept; inline friend constexpr bool operator!=(const cstring& lhs, const cstring& rhs) noexcept; @@ -105,12 +124,14 @@ class cstring final { }; inline constexpr bool operator==(const cstring& lhs, const cstring& rhs) noexcept { - if (lhs.size_ != rhs.size_) + if (lhs.size_ != rhs.size_) { return false; + } for (std::size_t i = 0; i < lhs.size_; ++i) { - if (lhs.str_[i] != rhs.str_[i]) + if (lhs.str_[i] != rhs.str_[i]) { return false; + } } return true; @@ -141,8 +162,9 @@ inline constexpr bool IsLexeme(char s) noexcept { } inline constexpr cstring NameofBase(const char* name, std::size_t length) noexcept { - if (length == 0) + if (length == 0) { return {name, length}; + } for (std::size_t i = length; i > 0; --i) { if (IsLexeme(name[i - 1])) { @@ -153,9 +175,10 @@ inline constexpr cstring NameofBase(const char* name, std::size_t length) noexce return {name, length}; } -inline constexpr cstring NameofFunction(const char* name, std::size_t length, bool with_prefix) noexcept { - if (length == 0) +inline constexpr cstring NameofFunction(const char* name, std::size_t length) noexcept { + if (length == 0) { return {name, length}; + } std::size_t h = 0; std::size_t p = 0; @@ -178,16 +201,17 @@ inline constexpr cstring NameofFunction(const char* name, std::size_t length, bo } if (IsLexeme(name[i - 1]) && h == 0) { - return {&name[i], length - i - (with_prefix ? 0 : p)}; + return {&name[i], length - i - p}; } } - return {name, length - (with_prefix ? 0 : p)}; + return {name, length - p}; } -inline constexpr cstring NameofType(const char* name, std::size_t length, bool with_prefix) noexcept { - if (length == 0) +inline constexpr cstring NameofType(const char* name, std::size_t length) noexcept { + if (length == 0) { return {name, length}; + } std::size_t h = 0; std::size_t p = 0; @@ -215,11 +239,11 @@ inline constexpr cstring NameofType(const char* name, std::size_t length, bool w } if (IsLexeme(name[i - 1]) && h == 0) { - return {&name[i], length - i - (with_prefix ? 0 : p)}; + return {&name[i], length - i - p}; } } - return {name, length - (with_prefix ? 0 : p)}; + return {name, length - p}; } inline constexpr cstring NameofRaw(const char* name, std::size_t length) noexcept { @@ -231,10 +255,11 @@ inline constexpr cstring NameofRaw(const char* name, std::size_t length) noexcep template ::value && !std::is_void::value>::type> -inline constexpr detail::cstring Nameof(const T&, const char* name, std::size_t length, bool with_prefix) noexcept { +inline constexpr detail::cstring Nameof(const T&, const char* name, std::size_t length) noexcept { // TODO: conditional expression is constant - if (std::is_function::value || std::is_member_function_pointer::value) - return detail::NameofFunction(name, length, with_prefix); + if (std::is_function::value || std::is_member_function_pointer::value) { + return detail::NameofFunction(name, length); + } return detail::NameofBase(name, length); } @@ -258,40 +283,33 @@ inline constexpr detail::cstring NameofType(bool full) noexcept { constexpr auto prefix_length = sizeof("class nameof::detail::cstring __cdecl nameof::NameofType<") - 1; constexpr auto suffix_length = sizeof(">(bool) noexcept") - 1; #endif + auto type_name = detail::cstring{function_name + prefix_length, total_length - prefix_length - suffix_length}; + type_name = full ? type_name : detail::NameofType(type_name.begin(), type_name.length()); + #if defined(_MSC_VER) constexpr auto class_length = sizeof("class") - 1; constexpr auto struct_length = sizeof("struct") - 1; constexpr auto enum_length = sizeof("enum") - 1; - if (std::is_class::type>::type>::value) { - if (type_name[0] == 'c') { - if (type_name[class_length] == ' ') { - type_name = detail::cstring{type_name.begin() + class_length + 1, type_name.length() - class_length - 1}; - } else { - type_name = detail::cstring{type_name.begin() + class_length, type_name.length() - class_length}; - } - } else if (type_name[0] == 's') { - if (type_name[struct_length] == ' ') { - type_name = detail::cstring{type_name.begin() + struct_length + 1, type_name.length() - struct_length - 1}; - } else { - type_name = detail::cstring{type_name.begin() + struct_length, type_name.length() - struct_length}; - } - } - } else if (std::is_enum::type>::type>::value) { - if (type_name[0] == 'e') { - if (type_name[enum_length] == ' ') { - type_name = detail::cstring{type_name.begin() + enum_length + 1, type_name.length() - enum_length - 1}; - } else { - type_name = detail::cstring{type_name.begin() + enum_length, type_name.length() - enum_length}; - } + using D = typename detail::remove_all_pointers::type>::type; + + if (!full && (std::is_class::value || std::is_enum::value) && (std::is_reference::value || std::is_pointer::value)) { + if (std::is_class::value && type_name[0] == 'c' && type_name[1] == 'l' && type_name[2] == 'a' && type_name[3] == 's' && type_name[4] == 's') { + type_name.remove_prefix(class_length); + } else if (std::is_class::value && type_name[0] == 's' && type_name[1] == 't' && type_name[2] == 'r' && type_name[3] == 'u' && type_name[4] == 'c' && type_name[5] == 't') { + type_name.remove_prefix(struct_length); + } else if(std::is_enum::value && type_name[0] == 'e' && type_name[1] == 'n' && type_name[2] == 'u' && type_name[3] == 'm') { + type_name.remove_prefix(enum_length); } + //type_name.remove_prefix(type_name[0] == ' ' ? 1 : 0); } + #endif - return full ? type_name : detail::NameofType(type_name.begin(), type_name.length(), false); + return type_name; } } // namespace nameof @@ -305,15 +323,10 @@ inline constexpr detail::cstring NameofType(bool full) noexcept { #endif // Used to obtain the simple (unqualified) string name of a variable, member, function. -#define NAMEOF(name) ::nameof::Nameof(name, #name, (sizeof(#name) / sizeof(char)) - 1, false) - -// Used to obtain the simple (unqualified) string name of a variable, member, function with template prefix. -#define NAMEOF_T(name) ::nameof::Nameof(name, #name, (sizeof(#name) / sizeof(char)) - 1, true) - -#define NAMEOF_FULL 0 +#define NAMEOF(name) ::nameof::Nameof(name, #name, (sizeof(#name) / sizeof(char)) - 1) // Used to obtain the simple (unqualified) string name of a type. #define NAMEOF_TYPE(name) ::nameof::NameofType(false) -// Used to obtain the full string name of a type. -#define NAMEOF_TYPE_FULL(name) ::nameof::NameofType(true) +// Used to obtain the raw string name of a type. +#define NAMEOF_TYPE_RAW(name) ::nameof::NameofType(true) diff --git a/test/test.cpp b/test/test.cpp index 2ef11d7..3b7b1cb 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -26,7 +26,6 @@ #include -#include #include #include @@ -50,10 +49,16 @@ T SomeMethod4() { template class SomeClass { public: - void SomeMethod5() const {} + void SomeMethod5() const { + std::cout << nameof::NameofType(false) << std::endl; + } template - C SomeMethod6() const { return C{}; } + C SomeMethod6() const { + C t{}; + std::cout << NAMEOF_TYPE(t) << std::endl; + return t; + } }; struct Long { @@ -67,8 +72,11 @@ enum class Color { RED, GREEN, BLUE }; SomeStruct somevar; Long othervar; +SomeStruct& refvar = somevar; SomeStruct* ptrvar = &somevar; +#if __cplusplus >= 201402L || (defined(_MSVC_LANG ) && _MSVC_LANG >= 201402L ) +// Compile-time supported by C++14. TEST_CASE("constexpr") { SECTION("NAMEOF") { // variable @@ -105,7 +113,39 @@ TEST_CASE("constexpr") { constexpr auto cx6 = NAMEOF_RAW(__cplusplus); static_assert(cx6 == "__cplusplus", ""); } + + SECTION("NAMEOF_TYPE") { + SomeClass a; + + constexpr auto cx1 = NAMEOF_TYPE(a); + static_assert(cx1 == "SomeClass", ""); + + constexpr auto cx2 = nameof::NameofType>(false); + static_assert(cx2 == "SomeClass", ""); + + constexpr auto cx3 = nameof::NameofType(false); + static_assert(cx3 == "SomeClass", ""); + } + + SECTION("NAMEOF_TYPE_RAW") { + SomeClass a; + + constexpr auto cx1 = NAMEOF_TYPE_RAW(a); + constexpr auto cx2 = nameof::NameofType>(true); + constexpr auto cx3 = nameof::NameofType(true); + +#if defined(_MSC_VER) + static_assert(cx1 == "class SomeClass", ""); + static_assert(cx2 == "class SomeClass", ""); + static_assert(cx3 == "class SomeClass", ""); +#else + static_assert(cx1 == "SomeClass", ""); + static_assert(cx2 == "SomeClass", ""); + static_assert(cx3 == "SomeClass", ""); +#endif + } } +#endif TEST_CASE("simple name") { SECTION("variable") { @@ -134,6 +174,7 @@ TEST_CASE("simple name") { REQUIRE(NAMEOF(Color::BLUE) == "BLUE"); } } + TEST_CASE("raw name") { SECTION("variable") { REQUIRE(NAMEOF_RAW(somevar) == "somevar"); @@ -184,42 +225,63 @@ TEST_CASE("raw name") { } } +TEST_CASE("type name") { + REQUIRE(NAMEOF_TYPE(somevar) == "SomeStruct"); + REQUIRE(NAMEOF_TYPE(ptrvar) == "SomeStruct"); + REQUIRE(NAMEOF_TYPE(refvar) == "SomeStruct"); + + REQUIRE(NAMEOF_TYPE(othervar) == "Long"); + REQUIRE(NAMEOF_TYPE(othervar.ll) == "LL"); + REQUIRE(NAMEOF_TYPE(othervar.ll.field) == "int"); + + REQUIRE(NAMEOF_TYPE(Color::RED) == "Color"); + + REQUIRE(NAMEOF_TYPE(std::string{}) == "basic_string"); + + REQUIRE(NAMEOF_TYPE(std::declval>()) == "SomeClass"); +} + +TEST_CASE("type raw name") { +#if defined(_MSC_VER) + REQUIRE(NAMEOF_TYPE_RAW(somevar) == "struct SomeStruct"); + REQUIRE(NAMEOF_TYPE_RAW(ptrvar) == "structSomeStruct*"); + REQUIRE(NAMEOF_TYPE_RAW(refvar) == "structSomeStruct&"); + + REQUIRE(NAMEOF_TYPE_RAW(othervar) == "struct Long"); + REQUIRE(NAMEOF_TYPE_RAW(othervar.ll) == "struct Long::LL"); + REQUIRE(NAMEOF_TYPE_RAW(othervar.ll.field) == "int"); + + REQUIRE(NAMEOF_TYPE_RAW(Color::RED) == "enum Color"); + + REQUIRE(NAMEOF_TYPE_RAW(std::declval>()) == "const classSomeClass&&"); +#else + REQUIRE(NAMEOF_TYPE_RAW(somevar) == "SomeStruct"); + REQUIRE(NAMEOF_TYPE_RAW(ptrvar) == "SomeStruct*"); + REQUIRE(NAMEOF_TYPE_RAW(refvar) == "SomeStruct&"); + + REQUIRE(NAMEOF_TYPE_RAW(othervar) == "Long"); + REQUIRE(NAMEOF_TYPE_RAW(othervar.ll) == "Long::LL"); + REQUIRE(NAMEOF_TYPE_RAW(othervar.ll.field) == "int"); + + REQUIRE(NAMEOF_TYPE_RAW(Color::RED) == "Color"); + + REQUIRE(NAMEOF_TYPE_RAW(std::declval>()) == "const SomeClass&&"); +#endif + +} + TEST_CASE("Spaces and Tabs ignored") { SECTION("Spaces") { - // variable REQUIRE(NAMEOF( somevar ) == "somevar"); REQUIRE(NAMEOF_RAW( somevar ) == "somevar"); - // member - REQUIRE(NAMEOF( (&somevar)->somefield ) == "somefield"); - REQUIRE(NAMEOF_RAW( (&somevar)->somefield ) == "(&somevar)->somefield"); - // type - REQUIRE(NAMEOF_RAW( std::string ) == "std::string"); - // function - REQUIRE(NAMEOF( &SomeStruct::SomeMethod2 ) == "SomeMethod2"); - REQUIRE(NAMEOF_RAW( &SomeStruct::SomeMethod2 ) == "&SomeStruct::SomeMethod2"); - // enum - REQUIRE(NAMEOF( Color::RED ) == "RED"); - REQUIRE(NAMEOF_RAW( Color::RED ) == "Color::RED"); - // macros - REQUIRE(NAMEOF_RAW( __cplusplus ) == "__cplusplus"); + REQUIRE(NAMEOF_TYPE( int{} ) == "int"); + REQUIRE(NAMEOF_TYPE_RAW( int{} ) == "int"); } SECTION("Tabs") { - // variable REQUIRE(NAMEOF( somevar ) == "somevar"); REQUIRE(NAMEOF_RAW( somevar ) == "somevar"); - // member - REQUIRE(NAMEOF( (&somevar)->somefield ) == "somefield"); - REQUIRE(NAMEOF_RAW( (&somevar)->somefield ) == "(&somevar)->somefield"); - // type - REQUIRE(NAMEOF_RAW( std::string ) == "std::string"); - // function - REQUIRE(NAMEOF( &SomeStruct::SomeMethod2 ) == "SomeMethod2"); - REQUIRE(NAMEOF_RAW( &SomeStruct::SomeMethod2 ) == "&SomeStruct::SomeMethod2"); - // enum - REQUIRE(NAMEOF( Color::RED ) == "RED"); - REQUIRE(NAMEOF_RAW( Color::RED ) == "Color::RED"); - // macros - REQUIRE(NAMEOF_RAW( __cplusplus ) == "__cplusplus"); + REQUIRE(NAMEOF_TYPE( int{} ) == "int"); + REQUIRE(NAMEOF_TYPE_RAW( int{} ) == "int"); } }