From 6fa49cca4b52cd2d22d297f8fab09ee4d578ab90 Mon Sep 17 00:00:00 2001 From: Daniil Goncharov Date: Tue, 29 Dec 2020 20:45:54 +0200 Subject: [PATCH] Wip v0.10.0 (#28) --- CMakeLists.txt | 28 +- README.md | 32 +- doc/limitations.md | 79 +--- doc/reference.md | 265 +++++++------- example/CMakeLists.txt | 15 +- example/example.cpp | 4 +- example/example_custom_name.cpp | 93 +++++ include/nameof.hpp | 406 ++++++++++++++------- test/test.cpp | 615 +++++++++++++++++++------------- 9 files changed, 916 insertions(+), 621 deletions(-) create mode 100644 example/example_custom_name.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e1fb456..5efe259 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.8) -project(nameof VERSION "0.9.4" LANGUAGES CXX) +project(nameof VERSION "0.9.5" LANGUAGES CXX) if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) set(IS_TOPLEVEL_PROJECT TRUE) @@ -10,6 +10,7 @@ endif() option(NAMEOF_OPT_BUILD_EXAMPLES "Build nameof examples" ${IS_TOPLEVEL_PROJECT}) option(NAMEOF_OPT_BUILD_TESTS "Build and perform nameof tests" ${IS_TOPLEVEL_PROJECT}) +option(NAMEOF_OPT_INSTALL "Generate and install nameof target" ${IS_TOPLEVEL_PROJECT}) if(NAMEOF_OPT_BUILD_EXAMPLES) add_subdirectory(example) @@ -33,18 +34,21 @@ write_basic_package_version_file(${PROJECT_NAME}ConfigVersion.cmake VERSION ${PROJECT_VERSION} COMPATIBILITY AnyNewerVersion) -install(TARGETS ${PROJECT_NAME} - EXPORT ${PROJECT_NAME}Config) +if(NAMEOF_OPT_INSTALL) + install(TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}Config) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake - DESTINATION lib/cmake/${PROJECT_NAME}) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + DESTINATION lib/cmake/${PROJECT_NAME}) -install(EXPORT ${PROJECT_NAME}Config - NAMESPACE ${PROJECT_NAME}:: - DESTINATION lib/cmake/${PROJECT_NAME}) + install(EXPORT ${PROJECT_NAME}Config + NAMESPACE ${PROJECT_NAME}:: + DESTINATION lib/cmake/${PROJECT_NAME}) -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include - DESTINATION .) + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include + DESTINATION .) + + export(EXPORT ${PROJECT_NAME}Config + NAMESPACE ${PROJECT_NAME}::) +endif() -export(EXPORT ${PROJECT_NAME}Config - NAMESPACE ${PROJECT_NAME}::) diff --git a/README.md b/README.md index a35dddf..1e7aa90 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ ``` [![Github releases](https://img.shields.io/github/release/Neargye/nameof.svg)](https://github.com/Neargye/nameof/releases) -[![Conan package](https://img.shields.io/badge/Conan-package-blueviolet)](https://conan.io/center/nameof/0.9.4) +[![Conan package](https://img.shields.io/badge/Conan-package-blueviolet)](https://conan.io/center/nameof) [![Vcpkg package](https://img.shields.io/badge/Vcpkg-package-blueviolet)](https://github.com/microsoft/vcpkg/tree/master/ports/nameof) [![License](https://img.shields.io/github/license/Neargye/nameof.svg)](LICENSE) [![Build status](https://travis-ci.org/Neargye/nameof.svg?branch=master)](https://travis-ci.org/Neargye/nameof) @@ -86,19 +86,29 @@ Header-only C++17 library provides nameof macros and functions to simply obtain * Nameof type ```cpp - using T = const int&; - T var = 42; + const my::detail::SomeClass& var_ref = var; // Name of variable type. - NAMEOF_TYPE_EXPR(var) -> "int" - NAMEOF_FULL_TYPE_EXPR(var) -> "const int&" - nameof::nameof_type() -> "int" - nameof::nameof_full_type() -> "const int&" + NAMEOF_TYPE_EXPR(var_ref) -> "my::detail::SomeClass" + nameof::nameof_type() -> "my::detail::SomeClass" + NAMEOF_FULL_TYPE_EXPR(var_ref) -> "const my::detail::SomeClass&" + nameof::nameof_full_type() -> "const my::detail::SomeClass&" + NAMEOF_SHORT_TYPE_EXPR(var_ref) -> "SomeClass" + nameof::nameof_short_type() -> "SomeClass" + using T = const my::detail::SomeClass&; // Name of type. - NAMEOF_TYPE(T) -> "int" - NAMEOF_FULL_TYPE(T) -> "const int&" - nameof::nameof_type() -> "int" - nameof::nameof_full_type() -> "const int&" + NAMEOF_TYPE(T) ->"my::detail::SomeClass" + nameof::nameof_type() -> "my::detail::SomeClass" + NAMEOF_FULL_TYPE(T) -> "const my::detail::SomeClass&" + nameof::nameof_full_type() -> "const my::detail::SomeClass&" + NAMEOF_SHORT_TYPE(T) -> "SomeClass" + nameof::nameof_short_type() -> "SomeClass" + + my::detail::Base* ptr = new my::detail::Derived(); + // Name of type, using rtti. + NAMEOF_TYPE_RTTI(*ptr) -> "my::detail::Derived" + NAMEOF_FULL_TYPE_RTTI(*ptr) -> "volatile const my::detail::Derived&" + NAMEOF_SHORT_TYPE_RTTI(*ptr) -> "Derived" ``` * Compile-time diff --git a/doc/limitations.md b/doc/limitations.md index dc60ac4..9938e06 100644 --- a/doc/limitations.md +++ b/doc/limitations.md @@ -6,23 +6,24 @@ ## Nameof Type -* To check is nameof type supported compiler use macro `NAMEOF_TYPE_SUPPORTED` or constexpr constant `nameof::is_nameof_type_supported`. - * This library uses a compiler-specific hack (based on `__PRETTY_FUNCTION__` / `__FUNCSIG__`), which works on Clang >= 5, MSVC >= 15.3 and GCC >= 7. -* Nameof type returns compiler-specific type name. +* nameof_type and nameof_type_rtti returns compiler-specific type name. -* If argument does not have name, occurs the compilation error `"Expression does not have a name."`. +* To check is nameof_type supported compiler use macro `NAMEOF_TYPE_SUPPORTED` or constexpr constant `nameof::is_nameof_type_supported`.
+ If nameof_type used on unsupported compiler, occurs the compilation error. To suppress error define macro `NAMEOF_TYPE_NO_CHECK_SUPPORT`. + +* To check is nameof_type_rtti supported compiler use macro `NAMEOF_TYPE_RTTI_SUPPORTED` or constexpr constant `nameof::is_nameof_type_rtti_supported`.
+ If nameof_type used on unsupported compiler, occurs the compilation error. To suppress error define macro `NAMEOF_TYPE_NO_CHECK_SUPPORT`. ## Nameof Enum -* Do not use [nameof](https://github.com/Neargye/nameof) and [magic_enum](https://github.com/Neargye/magic_enum) in the same project to get enum name. - -* To check is nameof enum supported compiler use macro `NAMEOF_ENUM_SUPPORTED` or constexpr constant `nameof::is_nameof_enum_supported`. - * This library uses a compiler-specific hack (based on `__PRETTY_FUNCTION__` / `__FUNCSIG__`), which works on Clang >= 5, MSVC >= 15.3 and GCC >= 9. -* Enum can't reflect if the enum is a forward declaration. +* Do not use [nameof](https://github.com/Neargye/nameof) and [magic_enum](https://github.com/Neargye/magic_enum) in the same project to get enum name. + +* To check is nameof_enum supported compiler use macro `NAMEOF_ENUM_SUPPORTED` or constexpr constant `nameof::is_nameof_enum_supported`.
+ If nameof_enum used on unsupported compiler, occurs the compilation error. To suppress error define macro `NAMEOF_ENUM_NO_CHECK_SUPPORT`. * Enum value must be in range `[NAMEOF_ENUM_RANGE_MIN, NAMEOF_ENUM_RANGE_MAX]`. @@ -40,74 +41,22 @@ #include ``` - * If need another range for specific enum type, add specialization `enum_range` for necessary enum type. Specialization of `enum_range` must be injected in `namespace nameof`. + * If need another range for specific enum type, add specialization `enum_range` for necessary enum type. Specialization of `enum_range` must be injected in `namespace nameof::customize`. ```cpp #include enum class number { one = 100, two = 200, three = 300 }; - namespace nameof { template <> - struct enum_range { + struct nameof::customize::enum_range { static constexpr int min = 100; static constexpr int max = 300; }; - } // namespace nameof ``` -* Nameof enum won't work if a value is aliased, work with enum-aliases is compiler-implementation-defined. +* Won't work if a value is aliased, work with enum-aliases is compiler-implementation-defined. - ```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::nameof_enum(ShapeKind::Box) -> "ConvexBegin" or "" - // NAMEOF_ENUM(ShapeKind::Box) -> "ConvexBegin" or "" - ``` - - One of the possible workaround 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::nameof_enum(ShapeKind::Box) -> "Box" - // NAMEOF_ENUM(ShapeKind::Box) -> "Box" - - // Non-reflected aliases. - // nameof::nameof_enum(ShapeKind::ConvexBegin) -> "Box" - // NAMEOF_ENUM(ShapeKind::ConvexBegin) -> "Box" - ``` - -* If you hit a message like this: - - ```text - [...] - note: constexpr evaluation hit maximum step limit; possible infinite loop? - ``` - - Change the limit for the number of constexpr evaluated: - * MSVC `/constexpr:depthN`, `/constexpr:stepsN` - * Clang `-fconstexpr-depth=N`, `-fconstexpr-steps=N` - * GCC `-fconstexpr-depth=N`, `-fconstexpr-loop-limit=N`, `-fconstexpr-ops-limit=N` +* Won't work if the enum is a forward declaration. * Intellisense Visual Studio may have some problems analyzing `nameof`. diff --git a/doc/reference.md b/doc/reference.md index f1972f3..bc25aed 100644 --- a/doc/reference.md +++ b/doc/reference.md @@ -1,38 +1,39 @@ # Reference -* [`NAMEOF` macro that obtains simple (unqualified) string name of variable, function, macro.](#nameof) -* [`NAMEOF_FULL` macro that obtains simple (unqualified) full (with template suffix) string name of variable, function, macro.](#nameof_full) -* [`NAMEOF_RAW` macro that obtains raw string name of variable, function, macro.](#nameof_raw) -* [`nameof_enum` function that obtains simple (unqualified) string name of enum variable.](#nameof_enum) -* [`NAMEOF_ENUM` macro that obtains simple (unqualified) string name of enum variable.](#nameof_enum-1) -* [`NAMEOF_ENUM_CONST` macro that obtains simple (unqualified) string name of static storage enum variable.](#nameof_enum_const) -* [`nameof_enum_flag` function that obtains simple (unqualified) string name of enum variable.](#nameof_enum_flag) -* [`NAMEOF_ENUM_FLAG` function that obtains simple (unqualified) string name of enum variable.](#nameof_enum_flag-1) -* [`nameof_type` function that obtains string name of type, reference and cv-qualifiers are ignored.](#nameof_type) -* [`nameof_full_type` function that obtains string name of full type, with reference and cv-qualifiers.](#nameof_full_type) -* [`NAMEOF_TYPE` macro that obtains string name of type, reference and cv-qualifiers are ignored.](#nameof_type-1) -* [`NAMEOF_FULL_TYPE` macro that obtains string name of full type, with reference and cv-qualifiers.](#nameof_full_type-1) -* [`NAMEOF_TYPE_EXPR` macro that obtains string name type of expression, reference and cv-qualifiers are ignored.](#nameof_type_expr) -* [`NAMEOF_FULL_TYPE_EXPR` macro that obtains string name full type of expression, with reference and cv-qualifiers.](#nameof_full_type_expr) -* [`NAMEOF_TYPE_RTTI` macro that obtains string name type, using RTTI.](#nameof_type_rtti) +* [`NAMEOF` obtains simple name of variable, function, macro.](#nameof) +* [`NAMEOF_FULL` obtains full name of variable, function, macro.](#nameof_full) +* [`NAMEOF_RAW` obtains raw name of variable, function, macro.](#nameof_raw) +* [`NAMEOF_ENUM` obtains name of enum variable.](#nameof_enum) +* [`NAMEOF_ENUM_CONST` obtains name of static storage enum variable.](#nameof_enum_const) +* [`NAMEOF_ENUM_FLAG` obtains name of enum-flags variable.](#nameof_enum_flag) +* [`NAMEOF_TYPE` obtains type name.](#nameof_type) +* [`NAMEOF_FULL_TYPE` obtains full type name.](#nameof_full_type) +* [`NAMEOF_SHORT_TYPE` obtains short type name.](#nameof_short_type) +* [`NAMEOF_TYPE_EXPR` obtains type name of expression.](#nameof_type_expr) +* [`NAMEOF_FULL_TYPE_EXPR` obtains full type name of expression.](#nameof_full_type_expr) +* [`NAMEOF_SHORT_TYPE_EXPR` obtains short type name of expression.](#nameof_short_type_expr) +* [`NAMEOF_TYPE_RTTI` obtains type name, using RTTI.](#nameof_type_rtti) +* [`NAMEOF_FULL_TYPE_RTTI` obtains short type name, using RTTI.](#nameof_full_type_rtti) +* [`NAMEOF_SHORT_TYPE_RTTI` obtains short type name, using RTTI.](#nameof_short_type_rtti) ## Synopsis * Before use, read the [limitations](limitations.md) of functionality. -* All functions `constexpr` and `noexcept`. - * To check is nameof_type supported compiler use macro `NAMEOF_TYPE_SUPPORTED` or constexpr constant `nameof::is_nameof_type_supported`.
If nameof_type used on unsupported compiler, occurs the compilation error. To suppress error define macro `NAMEOF_TYPE_NO_CHECK_SUPPORT`. +* To check is nameof_type_rtti supported compiler use macro `NAMEOF_TYPE_RTTI_SUPPORTED` or constexpr constant `nameof::is_nameof_type_rtti_supported`.
+ If nameof_type used on unsupported compiler, occurs the compilation error. To suppress error define macro `NAMEOF_TYPE_NO_CHECK_SUPPORT`. + * To check is nameof_enum supported compiler use macro `NAMEOF_ENUM_SUPPORTED` or constexpr constant `nameof::is_nameof_enum_supported`.
If nameof_enum used on unsupported compiler, occurs the compilation error. To suppress error define macro `NAMEOF_ENUM_NO_CHECK_SUPPORT`. ## `NAMEOF` -* Macro that obtains simple (unqualified) string name of variable, function, macro. +* Obtains simple (unqualified) name of variable, function, macro. -* Returns `nameof::cstring` - constexpr implementation of an string. +* Returns `nameof::cstring` - constexpr implementation of an string. Marked `constexpr` and `noexcept`. * If argument does not have name, occurs the compilation error `"Expression does not have a name."`. @@ -59,9 +60,9 @@ ## `NAMEOF_FULL` -* Macro that obtains simple (unqualified) full (with template suffix) string name of variable, function, macro. +* Obtains simple (unqualified) full (with template suffix) name of variable, function, macro. -* Returns `nameof::cstring` - constexpr implementation of an string. +* Returns `nameof::cstring` - constexpr implementation of an string. Marked `constexpr` and `noexcept`. * If argument does not have name, occurs the compilation error `"Expression does not have a name."`. @@ -77,9 +78,9 @@ ## `NAMEOF_RAW` -* Macro that obtains raw string name of variable, function, macro. +* Obtains raw name of variable, function, macro. -* Returns `nameof::cstring` - constexpr implementation of an string. +* Returns `nameof::cstring` - constexpr implementation of an string. Marked `constexpr` and `noexcept`. * If argument does not have name, occurs the compilation error `"Expression does not have a name."`. @@ -90,55 +91,28 @@ NAMEOF_RAW(&some_class::some_method) -> "&some_class::some_method" ``` -## `nameof_enum` - -* Function that obtains simple (unqualified) string name of enum variable. - -* Returns `std::string_view`. - -* Enum variable to string. - - * If argument does not have name or [out of range](limitations.md#nameof-enum), returns empty `std::string_view`. - - * Examples - - ```cpp - auto color = Color::RED; - nameof::nameof_enum(color) -> "RED" - ``` - -* Static storage enum variable to string. - - * This version is much lighter on the compile times and is not restricted to the enum_range [limitation](limitations.md#nameof-enum). - - * If argument does not have name, occurs the compilation error `"Enum value does not have a name."`. - - * Examples - - ```cpp - nameof::nameof_enum() -> "GREEN" - ``` ## `NAMEOF_ENUM` -* Macro that obtains simple (unqualified) string name of enum variable. +* Obtains simple (unqualified) name of enum variable. -* Returns `std::string_view`. +* Returns `string_view`. Marked `constexpr` and `noexcept`. -* If argument does not have name or [out of range](limitations.md#nameof-enum), returns empty `std::string_view`. +* If argument does not have name or [out of range](limitations.md#nameof-enum), returns empty `string_view`, in debug occurs assert. * Examples ```cpp auto color = Color::RED; NAMEOF_ENUM(color) -> "RED" + nameof::nameof_enum(color) -> "RED" ``` ## `NAMEOF_ENUM_CONST` -* Macro that obtains simple (unqualified) string name of static storage enum variable. +* Obtains simple (unqualified) name of static storage enum variable. -* Returns `std::string_view`. +* Returns `string_view`. Marked `constexpr` and `noexcept`. * This version is much lighter on the compile times and is not restricted to the enum_range [limitation](limitations.md#nameof-enum). @@ -148,133 +122,102 @@ ```cpp NAMEOF_ENUM_CONST(Color::GREEN) -> "GREEN" - ``` - -## `nameof_enum_flag` - -* Function that obtains simple (unqualified) string name of enum flag variable. - -* Returns `std::string`. - -* If argument does not have name or [out of range](limitations.md#nameof-enum), returns empty `std::string`. - -* Examples - - ```cpp - enum class AnimalFlags { HasClaws = 1 << 0, CanFly = 1 << 1, EatsFish = 1 << 2, Endangered = 1 << 3 }; - auto flag = AnimalFlags::Endangered; - nameof_enum_flag(flag) -> "Endangered" - flag = AnimalFlags::Endangered | AnimalFlags::CanFly; - nameof_enum_flag(flag) -> "CanFly|Endangered" - nameof_enum_flag(flag) -> "HasClaws|CanFly" - - nameof_enum(HasClaws | CanFly) -> "" + nameof::nameof_enum() -> "GREEN" ``` ## `NAMEOF_ENUM_FLAG` -* Macro that obtains simple (unqualified) string name of enum flag variable. +* Obtains simple (unqualified) name of enum flag variable. -* Returns `std::string`. +* Returns `string`. -* If argument does not have name or [out of range](limitations.md#nameof-enum), returns empty `std::string`. +* If argument does not have name or [out of range](limitations.md#nameof-enum), returns empty `string`, in debug occurs assert. * Examples ```cpp enum class AnimalFlags { HasClaws = 1 << 0, CanFly = 1 << 1, EatsFish = 1 << 2, Endangered = 1 << 3 }; auto flag = AnimalFlags::Endangered; + NAMEOF_ENUM_FLAG(flag) -> "Endangered" - flag = AnimalFlags::Endangered | AnimalFlags::CanFly; + nameof_enum_flag(flag) -> "Endangered" + + flag = AnimalFlags::CanFly | AnimalFlags::Endangered; NAMEOF_ENUM_FLAG(flag) -> "CanFly|Endangered" - NAMEOF_ENUM_FLAG(flag) -> "HasClaws|CanFly" + nameof_enum_flag(flag) -> "CanFly|Endangered" NAMEOF_ENUM(HasClaws | CanFly) -> "" - ``` - -## `nameof_type` - -* Function that obtains string name of type, reference and cv-qualifiers are ignored. - -* Returns `nameof::cstring` - constexpr implementation of an string. - -* In all cases, reference and cv-qualifiers are ignored by `nameof_type` (that is, `nameof_type() == nameof_type()`). - -* Returns compiler-specific type name. - -* If argument does not have name, occurs the compilation error `"Expression does not have a name."`. - -* Examples - - ```cpp - using T = const int&; - nameof::nameof_type() -> "int" - ``` - -## `nameof_full_type` - -* Function that obtains string name of full type, with reference and cv-qualifiers. - -* Returns `nameof::cstring` - constexpr implementation of an string. - -* Returns compiler-specific type name. - -* If argument does not have name, occurs the compilation error `"Expression does not have a name."`. - -* Examples - - ```cpp - using T = const int&; - nameof::nameof_full_type() -> "const int&" + nameof_enum(HasClaws | CanFly) -> "" ``` ## `NAMEOF_TYPE` -* Macro that obtains string name of type, reference and cv-qualifiers are ignored. +* Obtains type name, reference and cv-qualifiers are ignored. -* Returns `nameof::cstring` - constexpr implementation of an string. +* Returns `string_view`. Marked `constexpr` and `noexcept`. * In all cases, reference and cv-qualifiers are ignored by `NAMEOF_TYPE` (that is, `NAMEOF_TYPE(const T&) == NAMEOF_TYPE(T)`). * Returns compiler-specific type name. -* If argument does not have name, occurs the compilation error `"Expression does not have a name."`. +* If type does not have name, occurs the compilation error `"Type does not have a name."`. + * Examples ```cpp using T = const int&; NAMEOF_TYPE(T) -> "int" + nameof::nameof_type() -> "int" ``` ## `NAMEOF_FULL_TYPE` -* Macro that obtains string name of full type, with reference and cv-qualifiers. +* Obtains full type name, with reference and cv-qualifiers. -* Returns `nameof::cstring` - constexpr implementation of an string. +* Returns `string_view`. Marked `constexpr` and `noexcept`. * Returns compiler-specific type name. -* If argument does not have name, occurs the compilation error `"Expression does not have a name."`. +* If type does not have name, occurs the compilation error `"Type does not have a name."`. * Examples ```cpp using T = const int&; NAMEOF_TYPE(T) -> "const int&" + nameof::nameof_full_type() -> "const int&" + ``` + +## `NAMEOF_SHORT_TYPE` + +* Obtains short type name. + +* Returns `string_view`. Marked `constexpr` and `noexcept`. + +* Returns compiler-specific type name. + +* If type does not have name, occurs the compilation error `"Type does not have a name."`. + +* Examples + + ```cpp + using T = const my::detail::SomeClass&; + NAMEOF_SHORT_TYPE(T) -> "SomeClass" + nameof::nameof_short_type() -> "SomeClass" ``` ## `NAMEOF_TYPE_EXPR` -* Macro that obtains string name type of expression, reference and cv-qualifiers are ignored. +* Obtains string name type of expression, reference and cv-qualifiers are ignored. -* Returns `nameof::cstring` - constexpr implementation of an string. +* Returns `string_view`. Marked `constexpr` and `noexcept`. -* Returns returns compiler-specific type name. +* Returns compiler-specific type name. * In all cases, reference and cv-qualifiers are ignored. -* If argument does not have name, occurs the compilation error `"Expression does not have a name."`. +* If type does not have name, occurs the compilation error `"Type does not have a name."`. * Examples @@ -282,17 +225,18 @@ using T = const int&; T var = 42; NAMEOF_TYPE_EXPR(var) -> "int" + nameof::nameof_type() -> "int" ``` ## `NAMEOF_FULL_TYPE_EXPR` -* Macro that obtains string name full type of expression, with reference and cv-qualifiers. +* Obtains full type name of expression, with reference and cv-qualifiers. -* Returns `nameof::cstring` - constexpr implementation of an string. +* Returns `string_view`. Marked `constexpr` and `noexcept`. * Returns compiler-specific type name. -* If argument does not have name, occurs the compilation error `"Expression does not have a name."`. +* If type does not have name, occurs the compilation error `"Type does not have a name."`. * Examples @@ -300,21 +244,62 @@ using T = const int&; T var = 42; NAMEOF_FULL_TYPE_EXPR(var) -> "const int&" + nameof::nameof_full_type() -> "const int&" ``` -## `NAMEOF_TYPE_RTTI` +## `NAMEOF_SHORT_TYPE_EXPR` -* Macro that obtains string name of type, using RTTI. +* Obtains short type name of expression. -* Returns demangled RTTI type name. +* Returns `string_view`. Marked `constexpr` and `noexcept`. + +* Returns compiler-specific type name. + +* If type does not have name, occurs the compilation error `"Type does not have a name."`. * Examples ```cpp - struct Base { virtual void foo() {} }; - struct Derived : Base {}; - - Base* ptr = new Derived(); - NAMEOF_TYPE_RTTI(ptr) -> "Base *" - NAMEOF_TYPE_RTTI(*ptr) -> "Derived" + const my::detail::SomeClass var; + NAMEOF_SHORT_TYPE_EXPR(var) -> "SomeClass" + nameof::nameof_short_type() -> "SomeClass" + ``` + +## `NAMEOF_TYPE_RTTI` + +* Obtains type name, using RTTI. + +* Returns `string`. + +* Examples + + ```cpp + volatile const my::detail::Base* ptr = new my::detail::Derived(); + NAMEOF_TYPE_RTTI(*ptr) -> "my::detail::Derived" + ``` + +## `NAMEOF_FULL_TYPE_RTTI` + +* Obtains full type name, using RTTI. + +* Returns `string`. + +* Examples + + ```cpp + volatile const my::detail::Base* ptr = new my::detail::Derived(); + NAMEOF_FULL_TYPE_RTTI(cv_ref) -> "volatile const my::detail::Derived&" + `` + +## `NAMEOF_SHORT_TYPE_RTTI` + +* Obtains short type name, using RTTI. + +* Returns `string`. + +* Examples + + ```cpp + volatile const my::detail::Base* ptr = new my::detail::Derived(); + NAMEOF_SHORT_TYPE_RTTI(*ptr) -> "Derived" ``` diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 3c2a934..2c6ca8e 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -9,8 +9,13 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") endif() endif() -add_executable(example example.cpp) -set_target_properties(example PROPERTIES CXX_EXTENSIONS OFF) -target_compile_features(example PRIVATE cxx_std_17) -target_compile_options(example PRIVATE ${OPTIONS}) -target_link_libraries(example PRIVATE ${CMAKE_PROJECT_NAME}) +function(make_example target) + add_executable(${target} ${target}.cpp) + set_target_properties(${target} PROPERTIES CXX_EXTENSIONS OFF) + target_compile_features(${target} PRIVATE cxx_std_17) + target_compile_options(${target} PRIVATE ${OPTIONS}) + target_link_libraries(${target} PRIVATE ${CMAKE_PROJECT_NAME}) +endfunction() + +make_example(example) +make_example(example_custom_name) diff --git a/example/example.cpp b/example/example.cpp index aaf7759..3b85182 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -178,8 +178,8 @@ int main() { #if defined(NAMEOF_TYPE_RTTI_SUPPORTED) // Nameof type using RTTI. Base* ptr = new Derived(); - std::cout << NAMEOF_TYPE_RTTI(ptr) << std::endl; // "Base *" - std::cout << NAMEOF_TYPE_RTTI(*ptr) << std::endl; // "Derived" + std::cout << NAMEOF_TYPE_RTTI(ptr) << std::endl; // 'Base *' + std::cout << NAMEOF_TYPE_RTTI(*ptr) << std::endl; // 'Derived' #endif // Some more complex example. diff --git a/example/example_custom_name.cpp b/example/example_custom_name.cpp new file mode 100644 index 0000000..003b1b6 --- /dev/null +++ b/example/example_custom_name.cpp @@ -0,0 +1,93 @@ +// Licensed under the MIT License . +// SPDX-License-Identifier: MIT +// Copyright (c) 2020 Daniil Goncharov . +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include + +#include + +enum class Color : int { RED = -10, BLUE = 0, GREEN = 10 }; +enum class Numbers : int { One, Two, Three }; + +#if defined(NAMEOF_ENUM_SUPPORTED) +// Сustom definitions of names for enum. +// Specialization of `enum_name` must be injected in `namespace nameof::customize`. +template <> +constexpr std::string_view nameof::customize::enum_name(Color value) noexcept { + switch (value) { + case Color::RED: + return "the red color"; + case Color::BLUE: + return "The BLUE"; + case Color::GREEN: + return {}; // Empty string for default value. + } + return {}; // Empty string for unknow value. +} + +// Сustom definitions of names for enum. +// Specialization of `enum_name` must be injected in `namespace nameof::customize`. +template <> +constexpr std::string_view nameof::customize::enum_name(Numbers value) noexcept { + switch (value) { + case Numbers::One: + return "the one"; + default: + return {}; // Empty string for default or unknow value. + } +} +#endif + +// Сustom definitions of names for type. +// Specialization of `type_name` must be injected in `namespace nameof::customize`. +template <> +constexpr std::string_view nameof::customize::type_name() noexcept { + return "The Color"; +} + +class a1_test {}; +class a2_test {}; + +// Сustom definitions of names for type. +// Specialization of `type_name` must be injected in `namespace nameof::customize`. +template <> +constexpr std::string_view nameof::customize::type_name() noexcept { + return "Animal"; +} + +int main() { +#if defined(NAMEOF_ENUM_SUPPORTED) + std::cout << nameof::nameof_enum(Color::RED) << std::endl; // 'the red color' + std::cout << nameof::nameof_enum(Color::BLUE) << std::endl; // 'The BLUE' + std::cout << nameof::nameof_enum(Color::GREEN) << std::endl; // 'GREEN' + + std::cout << nameof::nameof_enum(Numbers::One) << std::endl; // 'the one' + std::cout << nameof::nameof_enum(Numbers::Two) << std::endl; // 'Two' + std::cout << nameof::nameof_enum(Numbers::Three) << std::endl; // 'Three' +#endif + + std::cout << nameof::nameof_type() << std::endl; // 'The Color' + std::cout << nameof::nameof_type() << std::endl; // 'Numbers' + std::cout << nameof::nameof_type() << std::endl; // 'Animal' + std::cout << nameof::nameof_type() << std::endl; // 'a2_test' + + return 0; +} diff --git a/include/nameof.hpp b/include/nameof.hpp index 7158e3f..ba2d0cb 100644 --- a/include/nameof.hpp +++ b/include/nameof.hpp @@ -5,7 +5,7 @@ // | |\ | (_| | | | | | | __/ (_) | | | |____|_| |_| // |_| \_|\__,_|_| |_| |_|\___|\___/|_| \_____| // https://github.com/Neargye/nameof -// version 0.9.4 +// version 0.9.5 // // Licensed under the MIT License . // SPDX-License-Identifier: MIT @@ -34,7 +34,7 @@ #define NAMEOF_VERSION_MAJOR 0 #define NAMEOF_VERSION_MINOR 9 -#define NAMEOF_VERSION_PATCH 4 +#define NAMEOF_VERSION_PATCH 5 #include #include @@ -118,15 +118,31 @@ namespace nameof { +// If need another string_view type, define the macro NAMEOF_USING_ALIAS_STRING_VIEW. +#if defined(NAMEOF_USING_ALIAS_STRING_VIEW) +NAMEOF_USING_ALIAS_STRING_VIEW +#else +using string_view = std::string_view; +#endif + +// If need another string type, define the macro NAMEOF_USING_ALIAS_STRING. +#if defined(NAMEOF_USING_ALIAS_STRING) +NAMEOF_USING_ALIAS_STRING +#else +using string = std::string; +#endif + +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. template struct enum_range { - static_assert(std::is_enum_v, "nameof::enum_range requires enum type."); + static_assert(std::is_enum_v, "nameof::customize::enum_range requires enum type."); inline static constexpr int min = NAMEOF_ENUM_RANGE_MIN; inline static constexpr int max = NAMEOF_ENUM_RANGE_MAX; - static_assert(max > min, "nameof::enum_range requires max > min."); + static_assert(max > min, "nameof::customize::enum_range requires max > min."); }; static_assert(NAMEOF_ENUM_RANGE_MIN <= 0, "NAMEOF_ENUM_RANGE_MIN must be less or equals than 0."); @@ -137,15 +153,24 @@ 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 cunstom 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."); + + return {}; +} + +// If need cunstom name for type, add specialization type_name for necessary type. +template +constexpr string_view type_name() noexcept { + return {}; +} + +} // namespace nameof::customize + template class [[nodiscard]] cstring { - static_assert(N > 0, "nameof::cstring requires size greater than 0."); - - std::array chars_; - - template - constexpr cstring(std::string_view str, std::index_sequence) noexcept : chars_{{str[I]..., '\0'}} {} - public: using value_type = const char; using size_type = std::size_t; @@ -161,8 +186,8 @@ class [[nodiscard]] cstring { using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; - constexpr explicit cstring(std::string_view str) noexcept : cstring{str, std::make_index_sequence{}} { - assert(str.size() == N); + constexpr explicit cstring(string_view str) noexcept : cstring{str, std::make_index_sequence{}} { + assert(str.size() > 0 && str.size() == N); } constexpr cstring() = delete; @@ -209,80 +234,84 @@ class [[nodiscard]] cstring { [[nodiscard]] constexpr bool empty() const noexcept { return false; } - [[nodiscard]] constexpr int compare(std::string_view str) const noexcept { - return std::string_view{data(), size()}.compare(str); + [[nodiscard]] constexpr int compare(string_view str) const noexcept { + return string_view{data(), size()}.compare(str); } [[nodiscard]] constexpr const char* c_str() const noexcept { return data(); } - template , typename Allocator = std::allocator> - [[nodiscard]] std::basic_string str() const { return {begin(), end()}; } + [[nodiscard]] string str() const { return {begin(), end()}; } - [[nodiscard]] constexpr operator std::string_view() const noexcept { return {data(), size()}; } + [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; } - [[nodiscard]] constexpr explicit operator const char*() const noexcept { return data(); } + [[nodiscard]] constexpr explicit operator const_pointer() const noexcept { return data(); } - template , typename Allocator = std::allocator> - [[nodiscard]] explicit operator std::basic_string() const { return {begin(), end()}; } + [[nodiscard]] explicit operator string() const { return {begin(), end()}; } + + private: + template + constexpr cstring(string_view str, std::index_sequence) noexcept : chars_{{str[I]..., '\0'}} {} + + std::array chars_; }; template -[[nodiscard]] constexpr bool operator==(const cstring& lhs, std::string_view rhs) noexcept { +[[nodiscard]] constexpr bool operator==(const cstring& lhs, string_view rhs) noexcept { return lhs.compare(rhs) == 0; } template -[[nodiscard]] constexpr bool operator==(std::string_view lhs, const cstring& rhs) noexcept { +[[nodiscard]] constexpr bool operator==(string_view lhs, const cstring& rhs) noexcept { return lhs.compare(rhs) == 0; } template -[[nodiscard]] constexpr bool operator!=(const cstring& lhs, std::string_view rhs) noexcept { +[[nodiscard]] constexpr bool operator!=(const cstring& lhs, string_view rhs) noexcept { return lhs.compare(rhs) != 0; } template -[[nodiscard]] constexpr bool operator!=(std::string_view lhs, const cstring& rhs) noexcept { +[[nodiscard]] constexpr bool operator!=(string_view lhs, const cstring& rhs) noexcept { return lhs.compare(rhs) != 0; } template -[[nodiscard]] constexpr bool operator>(const cstring& lhs, std::string_view rhs) noexcept { +[[nodiscard]] constexpr bool operator>(const cstring& lhs, string_view rhs) noexcept { return lhs.compare(rhs) > 0; } template -[[nodiscard]] constexpr bool operator>(std::string_view lhs, const cstring& rhs) noexcept { +[[nodiscard]] constexpr bool operator>(string_view lhs, const cstring& rhs) noexcept { return lhs.compare(rhs) > 0; } template -[[nodiscard]] constexpr bool operator>=(const cstring& lhs, std::string_view rhs) noexcept { +[[nodiscard]] constexpr bool operator>=(const cstring& lhs, string_view rhs) noexcept { return lhs.compare(rhs) >= 0; } template -[[nodiscard]] constexpr bool operator>=(std::string_view lhs, const cstring& rhs) noexcept { +[[nodiscard]] constexpr bool operator>=(string_view lhs, const cstring& rhs) noexcept { return lhs.compare(rhs) >= 0; } template -[[nodiscard]] constexpr bool operator<(const cstring& lhs, std::string_view rhs) noexcept { +[[nodiscard]] constexpr bool operator<(const cstring& lhs, string_view rhs) noexcept { return lhs.compare(rhs) < 0; } template -[[nodiscard]] constexpr bool operator<(std::string_view lhs, const cstring& rhs) noexcept { +[[nodiscard]] constexpr bool operator<(string_view lhs, const cstring& rhs) noexcept { return lhs.compare(rhs) < 0; } template -[[nodiscard]] constexpr bool operator<=(const cstring& lhs, std::string_view rhs) noexcept { +[[nodiscard]] constexpr bool operator<=(const cstring& lhs, string_view rhs) noexcept { return lhs.compare(rhs) <= 0; } template -[[nodiscard]] constexpr bool operator<=(std::string_view lhs, const cstring& rhs) noexcept { +[[nodiscard]] constexpr bool operator<=(string_view lhs, const cstring& rhs) noexcept { return lhs.compare(rhs) <= 0; } @@ -295,23 +324,9 @@ std::basic_ostream& operator<<(std::basic_ostream& o return os; } -// If need another optional type, define the macro NAMEOF_USING_ALIAS_STRING_VIEW. -#if defined(NAMEOF_USING_ALIAS_STRING_VIEW) -NAMEOF_USING_ALIAS_STRING_VIEW -#else -using string_view = std::string_view; -#endif - -// If need another optional type, define the macro NAMEOF_USING_ALIAS_STRING. -#if defined(NAMEOF_USING_ALIAS_STRING) -NAMEOF_USING_ALIAS_STRING -#else -using string = std::string; -#endif - namespace detail { -constexpr std::string_view pretty_name(std::string_view name, bool remove_template_suffix = true) noexcept { +constexpr string_view pretty_name(string_view name, bool remove_template_suffix = true) noexcept { if (name.size() >= 1 && (name[0] == '"' || name[0] == '\'')) { return {}; // Narrow multibyte string literal. } else if (name.size() >= 2 && name[0] == 'R' && (name[1] == '"' || name[1] == '\'')) { @@ -411,7 +426,7 @@ constexpr I log2(I value) noexcept { static_assert(std::is_integral_v, "nameof::detail::log2 requires integral type."); auto ret = I{0}; - for (; value > I{1}; value >>= I{1}, ++ret) {}; + for (; value > I{1}; value >>= I{1}, ++ret) {} return ret; } @@ -433,20 +448,27 @@ inline constexpr bool is_enum_v = std::is_enum_v && std::is_same_v constexpr auto n() noexcept { static_assert(is_enum_v, "nameof::detail::n requires enum type."); + constexpr auto custom_name = customize::enum_name(V); + + if constexpr (custom_name.empty()) { + static_cast(custom_name); #if defined(NAMEOF_ENUM_SUPPORTED) && NAMEOF_ENUM_SUPPORTED # if defined(__clang__) || defined(__GNUC__) - constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); + constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); # elif defined(_MSC_VER) - constexpr auto name = pretty_name({__FUNCSIG__, sizeof(__FUNCSIG__) - 17}); + constexpr auto name = pretty_name({__FUNCSIG__, sizeof(__FUNCSIG__) - 17}); # endif - if constexpr (name.size() > 0) { - return cstring{name}; - } else { - return std::string_view{}; - } + if constexpr (name.size() > 0) { + return cstring{name}; + } else { + return string_view{}; + } #else - return std::string_view{}; // Unsupported compiler. + return string_view{}; // Unsupported compiler. #endif + } else { + return cstring{custom_name}; + } } template @@ -466,7 +488,7 @@ constexpr int reflected_min() noexcept { if constexpr (IsFlags) { return 0; } else { - constexpr auto lhs = enum_range::min; + constexpr auto lhs = customize::enum_range::min; static_assert(lhs > (std::numeric_limits::min)(), "nameof::enum_range requires min must be greater than INT16_MIN."); constexpr auto rhs = (std::numeric_limits::min)(); @@ -485,7 +507,7 @@ constexpr int reflected_max() noexcept { if constexpr (IsFlags) { return std::numeric_limits::digits - 1; } else { - constexpr auto lhs = enum_range::max; + constexpr auto lhs = customize::enum_range::max; static_assert(lhs < (std::numeric_limits::max)(), "nameof::enum_range requires max must be less than INT16_MAX."); constexpr auto rhs = (std::numeric_limits::max)(); @@ -514,11 +536,23 @@ constexpr E value(std::size_t i) noexcept { } } +template +constexpr std::size_t values_count(const std::array& valid) noexcept { + auto count = std::size_t{0}; + for (std::size_t i = 0; i < valid.size(); ++i) { + if (valid[i]) { + ++count; + } + } + + return count; +} + template constexpr auto values(std::index_sequence) noexcept { static_assert(is_enum_v, "nameof::detail::values requires enum type."); constexpr std::array valid{{is_valid(I)>()...}}; - constexpr std::size_t count = ((valid[I] ? std::size_t{1} : std::size_t{0}) + ...); + constexpr std::size_t count = values_count(valid); std::array values{}; for (std::size_t i = 0, v = 0; v < count; ++i) { @@ -641,7 +675,15 @@ struct nameof_type_supported : std::false_type {}; #endif -#if defined(_MSC_VER) +template +struct nameof_type_rtti_supported +#if defined(NAMEOF_TYPE_RTTI_SUPPORTED) && NAMEOF_TYPE_RTTI_SUPPORTED || defined(NAMEOF_TYPE_NO_CHECK_SUPPORT) + : std::true_type {}; +#else + : std::false_type {}; +#endif + +#if defined(_MSC_VER) && !defined(__clang__) template struct identity { using type = T; @@ -654,37 +696,123 @@ using identity = T; template using remove_cvref_t = std::remove_cv_t>; +template +using enable_if_has_short_name_t = std::enable_if_t && !std::is_pointer_v, R>; + template constexpr auto n() noexcept { -#if defined(NAMEOF_TYPE_SUPPORTED) && NAMEOF_TYPE_SUPPORTED -# if defined(__clang__) - constexpr std::string_view name{__PRETTY_FUNCTION__ + 31, sizeof(__PRETTY_FUNCTION__) - 34}; -# elif defined(__GNUC__) - constexpr std::string_view name{__PRETTY_FUNCTION__ + 46, sizeof(__PRETTY_FUNCTION__) - 49}; -# elif defined(_MSC_VER) - constexpr std::string_view name{__FUNCSIG__ + 63, sizeof(__FUNCSIG__) - 81 - (__FUNCSIG__[sizeof(__FUNCSIG__) - 19] == ' ' ? 1 : 0)}; +# if defined(_MSC_VER) && !defined(__clang__) + constexpr auto custom_name = customize::type_name(); +#else + constexpr auto custom_name = customize::type_name(); # endif - return cstring{name}; + if constexpr (custom_name.empty()) { + static_cast(custom_name); +#if defined(NAMEOF_TYPE_SUPPORTED) && NAMEOF_TYPE_SUPPORTED +# if defined(__clang__) + constexpr string_view name{__PRETTY_FUNCTION__ + 31, sizeof(__PRETTY_FUNCTION__) - 34}; +# elif defined(__GNUC__) + constexpr string_view name{__PRETTY_FUNCTION__ + 46, sizeof(__PRETTY_FUNCTION__) - 49}; +# elif defined(_MSC_VER) + constexpr string_view name{__FUNCSIG__ + 63, sizeof(__FUNCSIG__) - 81 - (__FUNCSIG__[sizeof(__FUNCSIG__) - 19] == ' ' ? 1 : 0)}; +# endif + + return cstring{name}; #else - return std::string_view{}; // Unsupported compiler. + return string_view{}; // Unsupported compiler. #endif + } else { + return cstring{custom_name}; + } } template inline constexpr auto type_name_v = n(); #if __has_include() -inline std::string demangle(const char* tn) { - auto dmg = abi::__cxa_demangle(tn, nullptr, nullptr, nullptr); - auto r = std::string{dmg}; +template +string nameof_type_rtti(const char* tn) { + static_assert(nameof_type_rtti_supported::value, "nameof::nameof_type_rtti unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + const auto dmg = abi::__cxa_demangle(tn, nullptr, nullptr, nullptr); + const auto name = string{dmg}; std::free(dmg); + assert(name.size() > 0 && "Type does not have a name."); - return r; + return name; +} + +template +string nameof_full_type_rtti(const char* tn) { +static_assert(nameof_type_rtti_supported::value, "nameof::nameof_type_rtti unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + const auto dmg = abi::__cxa_demangle(tn, nullptr, nullptr, nullptr); + auto name = string{dmg}; + std::free(dmg); + assert(name.size() > 0 && "Type does not have a name."); + if constexpr (std::is_const_v>) { + name = "const " + name; + } + if constexpr (std::is_volatile_v>) { + name = "volatile " + name; + } + if constexpr (std::is_lvalue_reference_v) { + name += '&'; + } + if constexpr (std::is_rvalue_reference_v) { + name += "&&"; + } + + return name; +} + +template = 0> +string nameof_short_type_rtti(const char* tn) { + static_assert(nameof_type_rtti_supported::value, "nameof::nameof_type_rtti unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + const auto dmg = abi::__cxa_demangle(tn, nullptr, nullptr, nullptr); + const auto name = string{pretty_name(dmg)}; + std::free(dmg); + assert(name.size() > 0 && "Type does not have a short name."); + + return name; } #else -constexpr std::string_view demangle(const char* tn) noexcept { - return {tn}; +template +string nameof_type_rtti(const char* tn) noexcept { + static_assert(nameof_type_rtti_supported::value, "nameof::nameof_type_rtti unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + const auto name = string_view{tn}; + assert(name.size() > 0 && "Type does not have a name."); + + return {name.begin(), name.end()}; +} + +template +string nameof_full_type_rtti(const char* tn) noexcept { +static_assert(nameof_type_rtti_supported::value, "nameof::nameof_type_rtti unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + auto name = string{tn}; + assert(name.size() > 0 && "Type does not have a name."); + if constexpr (std::is_const_v>) { + name = "const " + name; + } + if constexpr (std::is_volatile_v>) { + name = "volatile " + name; + } + if constexpr (std::is_lvalue_reference_v) { + name += '&'; + } + if constexpr (std::is_rvalue_reference_v) { + name += "&&"; + } + + return name; +} + +template = 0> +string nameof_short_type_rtti(const char* tn) noexcept { + static_assert(nameof_type_rtti_supported::value, "nameof::nameof_type_rtti unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); + const auto name = pretty_name(tn); + assert(name.size() > 0 && "Type does not have a short name."); + + return {name.begin(), name.end()}; } #endif @@ -693,19 +821,22 @@ constexpr std::string_view demangle(const char* tn) noexcept { // Checks is nameof_type supported compiler. inline constexpr bool is_nameof_type_supported = detail::nameof_type_supported::value; +// Checks is nameof_type_rtti supported compiler. +inline constexpr bool is_nameof_type_rtti_supported = detail::nameof_type_rtti_supported::value; + // Checks is nameof_enum supported compiler. inline constexpr bool is_nameof_enum_supported = detail::nameof_enum_supported::value; -// Obtains simple (unqualified) string name of enum variable. +// Obtains simple (unqualified) name of enum variable. template -[[nodiscard]] constexpr auto nameof_enum(E value) noexcept -> detail::enable_if_enum_t { +[[nodiscard]] constexpr auto nameof_enum(E value) noexcept -> detail::enable_if_enum_t { using D = std::decay_t; using U = std::underlying_type_t; 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 (const auto i = static_cast(value) - detail::min_v; static_cast(value) >= static_cast(detail::min_v) && - static_cast(value) <= static_cast(detail::max_v)) { + const bool valid = static_cast(value) >= static_cast(detail::min_v) && static_cast(value) <= static_cast(detail::max_v); + if (const auto i = static_cast(value) - detail::min_v; valid) { if constexpr (detail::is_sparse_v) { if (const auto idx = detail::indexes_v[i]; idx != detail::invalid_index_v) { return detail::strings_v[idx]; @@ -715,19 +846,20 @@ template } } + assert(valid && "enum variable does not have a name."); return {}; // Value out of range. } -// Obtains simple (unqualified) string name of enum-flags variable. +// Obtains simple (unqualified) name of enum-flags variable. template -[[nodiscard]] auto nameof_enum_flag(E value) -> detail::enable_if_enum_t { +[[nodiscard]] auto nameof_enum_flag(E value) -> detail::enable_if_enum_t { using D = std::decay_t; 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; - std::string name; + 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) { @@ -743,117 +875,125 @@ template } } - if (check_value != 0 && check_value == static_cast(value)) { + const bool valid = check_value != 0 && check_value == static_cast(value); + if (valid) { return name; } + assert(valid && "enum-flags variable does not have a name."); return {}; // Invalid value or out of range. } -// Obtains simple (unqualified) string name of static storage enum variable. +// Obtains simple (unqualified) name of static storage enum variable. // This version is much lighter on the compile times and is not restricted to the enum_range limitation. template -[[nodiscard]] constexpr auto nameof_enum() noexcept -> detail::enable_if_enum_t { +[[nodiscard]] constexpr auto nameof_enum() noexcept -> detail::enable_if_enum_t { using D = std::decay_t; static_assert(detail::nameof_enum_supported::value, "nameof::nameof_enum unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); - constexpr std::string_view name = detail::enum_name_v; + constexpr string_view name = detail::enum_name_v; static_assert(name.size() > 0, "Enum value does not have a name."); return name; } -// Obtains string name of type, reference and cv-qualifiers are ignored. +// Obtains name of type, reference and cv-qualifiers are ignored. template -[[nodiscard]] constexpr std::string_view nameof_type() noexcept { +[[nodiscard]] constexpr string_view nameof_type() noexcept { static_assert(detail::nameof_type_supported::value, "nameof::nameof_type unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); using U = detail::identity>; - constexpr std::string_view name = detail::type_name_v; + constexpr string_view name = detail::type_name_v; static_assert(name.size() > 0, "Type does not have a name."); return name; } -// Obtains string name of full type, with reference and cv-qualifiers. +// Obtains full name of type, with reference and cv-qualifiers. template -[[nodiscard]] constexpr std::string_view nameof_full_type() noexcept { +[[nodiscard]] constexpr string_view nameof_full_type() noexcept { static_assert(detail::nameof_type_supported::value, "nameof::nameof_type unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); using U = detail::identity; - constexpr std::string_view name = detail::type_name_v; - static_assert(name.size() > 0, "Type does not have a name."); + constexpr string_view name = detail::type_name_v; + static_assert(name.size() > 0, "Type does not have a full name."); return name; } -// Obtains string name of short type. +// Obtains short name of type. template -[[nodiscard]] constexpr std::string_view nameof_short_type() noexcept { +[[nodiscard]] constexpr auto nameof_short_type() noexcept -> detail::enable_if_has_short_name_t { static_assert(detail::nameof_type_supported::value, "nameof::nameof_type unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); using U = detail::identity>; - constexpr std::string_view name = detail::pretty_name(detail::type_name_v); - static_assert(name.size() > 0, "Type does not have a name."); + constexpr string_view name = detail::pretty_name(detail::type_name_v); + static_assert(name.size() > 0, "Type does not have a short name."); return name; } } // namespace nameof -// Obtains simple (unqualified) string name of variable, function, macro. -#define NAMEOF(...) []() constexpr noexcept { \ +// Obtains name of variable, function, macro. +#define NAMEOF(...) []() constexpr noexcept { \ + ::std::void_t(); \ + constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \ + static_assert(_name.size() > 0, "Expression does not have a name."); \ + constexpr auto _size = _name.size(); \ + constexpr auto _nameof = ::nameof::cstring<_size>{_name}; \ + return _nameof; }() + +// Obtains full name of variable, function, macro. +#define NAMEOF_FULL(...) []() constexpr noexcept { \ ::std::void_t(); \ - constexpr auto __name = ::nameof::detail::pretty_name(#__VA_ARGS__, true); \ - static_assert(__name.size() > 0, "Expression does not have a name."); \ - constexpr auto __size = __name.size(); \ - constexpr auto __nameof = ::nameof::cstring<__size>{__name}; \ - return __nameof; }() + constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__, false); \ + static_assert(_name.size() > 0, "Expression does not have a name."); \ + constexpr auto _size = _name.size(); \ + constexpr auto _nameof_full = ::nameof::cstring<_size>{_name}; \ + return _nameof_full; }() -// Obtains simple (unqualified) full (with template suffix) string name of variable, function, macro. -#define NAMEOF_FULL(...) []() constexpr noexcept { \ - ::std::void_t(); \ - constexpr auto __name = ::nameof::detail::pretty_name(#__VA_ARGS__, false); \ - static_assert(__name.size() > 0, "Expression does not have a name."); \ - constexpr auto __size = __name.size(); \ - constexpr auto __nameof_full = ::nameof::cstring<__size>{__name}; \ - return __nameof_full; }() +// Obtains raw name of variable, function, macro. +#define NAMEOF_RAW(...) []() constexpr noexcept { \ + ::std::void_t(); \ + constexpr auto _name = ::nameof::string_view{#__VA_ARGS__}; \ + static_assert(_name.size() > 0, "Expression does not have a name."); \ + constexpr auto _size = _name.size(); \ + constexpr auto _nameof_raw = ::nameof::cstring<_size>{_name}; \ + return _nameof_raw; }() -// Obtains raw string name of variable, function, macro. -#define NAMEOF_RAW(...) []() constexpr noexcept { \ - ::std::void_t(); \ - constexpr auto __name = ::std::string_view{#__VA_ARGS__}; \ - static_assert(__name.size() > 0, "Expression does not have a name."); \ - constexpr auto __size = __name.size(); \ - constexpr auto __nameof_raw = ::nameof::cstring<__size>{__name}; \ - return __nameof_raw; }() - -// Obtains simple (unqualified) string name of enum variable. +// Obtains name of enum variable. #define NAMEOF_ENUM(...) ::nameof::nameof_enum<::std::decay_t>(__VA_ARGS__) -// Obtains simple (unqualified) string name of static storage enum variable. +// Obtains name of static storage enum variable. // This version is much lighter on the compile times and is not restricted to the enum_range limitation. #define NAMEOF_ENUM_CONST(...) ::nameof::nameof_enum<__VA_ARGS__>() -// Obtains simple (unqualified) string name of enum-flags variable. +// Obtains name of enum-flags variable. #define NAMEOF_ENUM_FLAG(...) ::nameof::nameof_enum_flag<::std::decay_t>(__VA_ARGS__) -// Obtains string name of type, reference and cv-qualifiers are ignored. +// Obtains type name, reference and cv-qualifiers are ignored. #define NAMEOF_TYPE(...) ::nameof::nameof_type<__VA_ARGS__>() -// Obtains string name type of expression, reference and cv-qualifiers are ignored. +// Obtains type name of expression, reference and cv-qualifiers are ignored. #define NAMEOF_TYPE_EXPR(...) ::nameof::nameof_type() -// Obtains string name of full type, with reference and cv-qualifiers. +// Obtains full type name, with reference and cv-qualifiers. #define NAMEOF_FULL_TYPE(...) ::nameof::nameof_full_type<__VA_ARGS__>() -// Obtains string name full type of expression, with reference and cv-qualifiers. +// Obtains full type name of expression, with reference and cv-qualifiers. #define NAMEOF_FULL_TYPE_EXPR(...) ::nameof::nameof_full_type() -// Obtains string name of short type. +// Obtains short type name. #define NAMEOF_SHORT_TYPE(...) ::nameof::nameof_short_type<__VA_ARGS__>() -// Obtains string name short type of expression. +// Obtains short type name of expression. #define NAMEOF_SHORT_TYPE_EXPR(...) ::nameof::nameof_short_type() -// Obtains string name of type, using RTTI. -#define NAMEOF_TYPE_RTTI(...) ::nameof::detail::demangle(typeid(__VA_ARGS__).name()) +// Obtains type name, with reference and cv-qualifiers, using RTTI. +#define NAMEOF_TYPE_RTTI(...) ::nameof::detail::nameof_type_rtti(typeid(__VA_ARGS__).name()) + +// Obtains full type name, using RTTI. +#define NAMEOF_FULL_TYPE_RTTI(...) ::nameof::detail::nameof_full_type_rtti(typeid(__VA_ARGS__).name()) + +// Obtains short type name, using RTTI. +#define NAMEOF_SHORT_TYPE_RTTI(...) ::nameof::detail::nameof_short_type_rtti(typeid(__VA_ARGS__).name()) #if defined(__clang__) # pragma clang diagnostic pop diff --git a/test/test.cpp b/test/test.cpp index 90a33a7..ff7fd9f 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -30,6 +30,12 @@ #include #include +#ifdef NDEBUG +# define NAMEOF_DEBUG_REQUIRE(...) REQUIRE(__VA_ARGS__) +#else +# define NAMEOF_DEBUG_REQUIRE(...) +#endif + struct SomeStruct { int somefield = 0; @@ -93,15 +99,18 @@ enum class BigFlags : std::uint64_t { D = (static_cast(0x1) << 63), }; -namespace nameof { template <> -struct enum_range { +struct nameof::customize::enum_range { static_assert(std::is_enum_v, "nameof::enum_range requires enum type."); static constexpr int min = 100; static constexpr int max = 300; static_assert(max > min, "nameof::enum_range requires max > min."); }; -} // namespace nameof + +struct TestRtti{ + struct Base { virtual ~Base() = default; }; + struct Derived : Base {}; +}; SomeStruct struct_var; Long othervar; @@ -243,70 +252,6 @@ TEST_CASE("NAMEOF_RAW") { static_assert(nameof::is_nameof_enum_supported, "nameof::nameof_enum: Unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); -TEST_CASE("NAMEOF_ENUM") { - constexpr Color cr = Color::RED; - constexpr auto cr_name = NAMEOF_ENUM(cr); - Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; - REQUIRE(cr_name == "RED"); - REQUIRE(NAMEOF_ENUM(Color::BLUE) == "BLUE"); - REQUIRE(NAMEOF_ENUM(cm[1]) == "GREEN"); - REQUIRE(NAMEOF_ENUM(static_cast(0)).empty()); - - constexpr Numbers no = Numbers::one; - constexpr auto no_name = NAMEOF_ENUM(no); - REQUIRE(no_name == "one"); - REQUIRE(NAMEOF_ENUM(Numbers::two) == "two"); - REQUIRE(NAMEOF_ENUM(Numbers::three) == "three"); - REQUIRE(NAMEOF_ENUM(Numbers::many).empty()); - REQUIRE(NAMEOF_ENUM(static_cast(0)).empty()); - - constexpr Directions dr = Directions::Right; - constexpr auto dr_name = NAMEOF_ENUM(dr); - REQUIRE(NAMEOF_ENUM(Directions::Up) == "Up"); - REQUIRE(NAMEOF_ENUM(Directions::Down) == "Down"); - REQUIRE(dr_name == "Right"); - REQUIRE(NAMEOF_ENUM(Directions::Left) == "Left"); - REQUIRE(NAMEOF_ENUM(static_cast(0)).empty()); - - constexpr number nt = number::three; - constexpr auto nt_name = NAMEOF_ENUM(nt); - REQUIRE(NAMEOF_ENUM(number::one) == "one"); - REQUIRE(NAMEOF_ENUM(number::two) == "two"); - REQUIRE(nt_name == "three"); - REQUIRE(NAMEOF_ENUM(number::four).empty()); - REQUIRE(NAMEOF_ENUM(static_cast(0)).empty()); -} - -TEST_CASE("NAMEOF_ENUM_CONST") { - constexpr Color cr = Color::RED; - constexpr auto cr_name = NAMEOF_ENUM_CONST(cr); - constexpr Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; - REQUIRE(cr_name == "RED"); - REQUIRE(NAMEOF_ENUM_CONST(Color::BLUE) == "BLUE"); - REQUIRE(NAMEOF_ENUM_CONST(cm[1]) == "GREEN"); - - constexpr Numbers no = Numbers::one; - constexpr auto no_name = NAMEOF_ENUM_CONST(no); - REQUIRE(no_name == "one"); - REQUIRE(NAMEOF_ENUM_CONST(Numbers::two) == "two"); - REQUIRE(NAMEOF_ENUM_CONST(Numbers::three) == "three"); - REQUIRE(NAMEOF_ENUM_CONST(Numbers::many) == "many"); - - constexpr Directions dr = Directions::Right; - constexpr auto dr_name = NAMEOF_ENUM_CONST(dr); - REQUIRE(NAMEOF_ENUM_CONST(Directions::Up) == "Up"); - REQUIRE(NAMEOF_ENUM_CONST(Directions::Down) == "Down"); - REQUIRE(dr_name == "Right"); - REQUIRE(NAMEOF_ENUM_CONST(Directions::Left) == "Left"); - - constexpr number nt = number::three; - constexpr auto nt_name = NAMEOF_ENUM_CONST(nt); - REQUIRE(NAMEOF_ENUM_CONST(number::one) == "one"); - REQUIRE(NAMEOF_ENUM_CONST(number::two) == "two"); - REQUIRE(nt_name == "three"); - REQUIRE(NAMEOF_ENUM_CONST(number::four) == "four"); -} - TEST_CASE("nameof_enum") { SECTION("automatic storage") { constexpr Color cr = Color::RED; @@ -315,15 +260,15 @@ TEST_CASE("nameof_enum") { REQUIRE(cr_name == "RED"); REQUIRE(nameof::nameof_enum(Color::BLUE) == "BLUE"); REQUIRE(nameof::nameof_enum(cm[1]) == "GREEN"); - REQUIRE(nameof::nameof_enum(static_cast(0)).empty()); + NAMEOF_DEBUG_REQUIRE(nameof::nameof_enum(static_cast(0)).empty()); constexpr Numbers no = Numbers::one; constexpr auto no_name = nameof::nameof_enum(no); REQUIRE(no_name == "one"); REQUIRE(nameof::nameof_enum(Numbers::two) == "two"); REQUIRE(nameof::nameof_enum(Numbers::three) == "three"); - REQUIRE(nameof::nameof_enum(Numbers::many).empty()); - REQUIRE(nameof::nameof_enum(static_cast(0)).empty()); + NAMEOF_DEBUG_REQUIRE(nameof::nameof_enum(Numbers::many).empty()); + NAMEOF_DEBUG_REQUIRE(nameof::nameof_enum(static_cast(0)).empty()); constexpr Directions dr = Directions::Right; constexpr auto dr_name = nameof::nameof_enum(dr); @@ -331,15 +276,15 @@ TEST_CASE("nameof_enum") { REQUIRE(nameof::nameof_enum(Directions::Down) == "Down"); REQUIRE(dr_name == "Right"); REQUIRE(nameof::nameof_enum(Directions::Left) == "Left"); - REQUIRE(nameof::nameof_enum(static_cast(0)).empty()); + NAMEOF_DEBUG_REQUIRE(nameof::nameof_enum(static_cast(0)).empty()); constexpr number nt = number::three; constexpr auto nt_name = nameof::nameof_enum(nt); REQUIRE(nameof::nameof_enum(number::one) == "one"); REQUIRE(nameof::nameof_enum(number::two) == "two"); REQUIRE(nt_name == "three"); - REQUIRE(nameof::nameof_enum(number::four).empty()); - REQUIRE(nameof::nameof_enum(static_cast(0)).empty()); + NAMEOF_DEBUG_REQUIRE(nameof::nameof_enum(number::four).empty()); + NAMEOF_DEBUG_REQUIRE(nameof::nameof_enum(static_cast(0)).empty()); } SECTION("static storage") { @@ -380,11 +325,11 @@ TEST_CASE("nameof_enum_flag") { REQUIRE(af_name == "HasClaws"); REQUIRE(nameof::nameof_enum_flag(AnimalFlags::EatsFish) == "EatsFish"); REQUIRE(nameof::nameof_enum_flag(afm[1]) == "CanFly"); - REQUIRE(nameof::nameof_enum_flag(static_cast(0)).empty()); + NAMEOF_DEBUG_REQUIRE(nameof::nameof_enum_flag(static_cast(0)).empty()); REQUIRE(nameof::nameof_enum_flag(static_cast(1 | 2)) == "HasClaws|CanFly"); REQUIRE(nameof::nameof_enum_flag(static_cast(1 | 2 | 4)) == "HasClaws|CanFly|EatsFish"); REQUIRE(nameof::nameof_enum_flag(static_cast(1 | 0 | 8)) == "HasClaws|Endangered"); - REQUIRE(nameof::nameof_enum_flag(static_cast(0)).empty()); + NAMEOF_DEBUG_REQUIRE(nameof::nameof_enum_flag(static_cast(0)).empty()); constexpr BigFlags bf = BigFlags::A; auto bf_name = nameof::nameof_enum_flag(bf); @@ -392,15 +337,79 @@ TEST_CASE("nameof_enum_flag") { REQUIRE(bf_name == "A"); REQUIRE(nameof::nameof_enum_flag(BigFlags::C) == "C"); REQUIRE(nameof::nameof_enum_flag(bfm[1]) == "B"); - REQUIRE(nameof::nameof_enum_flag(static_cast(0)).empty()); - REQUIRE(nameof::nameof_enum_flag(static_cast(1 | 2)).empty()); + NAMEOF_DEBUG_REQUIRE(nameof::nameof_enum_flag(static_cast(0)).empty()); + NAMEOF_DEBUG_REQUIRE(nameof::nameof_enum_flag(static_cast(1 | 2)).empty()); REQUIRE(nameof::nameof_enum_flag(static_cast(1 | (static_cast(0x1) << 20))) == "A|B"); REQUIRE(nameof::nameof_enum_flag(static_cast(1 | (static_cast(0x1) << 20) | (static_cast(0x1) << 63))) == "A|B|D"); REQUIRE(nameof::nameof_enum_flag(static_cast(1 | 0 | (static_cast(0x1) << 40))) == "A|C"); REQUIRE(nameof::nameof_enum_flag(static_cast(1 | 0 | (static_cast(0x1) << 40))) == "A|C"); REQUIRE(nameof::nameof_enum_flag(static_cast((static_cast(0x1) << 63) | 1)) == "A|D"); - REQUIRE(nameof::nameof_enum_flag(static_cast(2)).empty()); - REQUIRE(nameof::nameof_enum_flag(static_cast((static_cast(0x1) << 63) | 2)).empty()); + NAMEOF_DEBUG_REQUIRE(nameof::nameof_enum_flag(static_cast(2)).empty()); + NAMEOF_DEBUG_REQUIRE(nameof::nameof_enum_flag(static_cast((static_cast(0x1) << 63) | 2)).empty()); +} + +TEST_CASE("NAMEOF_ENUM") { + constexpr Color cr = Color::RED; + constexpr auto cr_name = NAMEOF_ENUM(cr); + Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; + REQUIRE(cr_name == "RED"); + REQUIRE(NAMEOF_ENUM(Color::BLUE) == "BLUE"); + REQUIRE(NAMEOF_ENUM(cm[1]) == "GREEN"); + NAMEOF_DEBUG_REQUIRE(NAMEOF_ENUM(static_cast(0)).empty()); + + constexpr Numbers no = Numbers::one; + constexpr auto no_name = NAMEOF_ENUM(no); + REQUIRE(no_name == "one"); + REQUIRE(NAMEOF_ENUM(Numbers::two) == "two"); + REQUIRE(NAMEOF_ENUM(Numbers::three) == "three"); + NAMEOF_DEBUG_REQUIRE(NAMEOF_ENUM(Numbers::many).empty()); + NAMEOF_DEBUG_REQUIRE(NAMEOF_ENUM(static_cast(0)).empty()); + + constexpr Directions dr = Directions::Right; + constexpr auto dr_name = NAMEOF_ENUM(dr); + REQUIRE(NAMEOF_ENUM(Directions::Up) == "Up"); + REQUIRE(NAMEOF_ENUM(Directions::Down) == "Down"); + REQUIRE(dr_name == "Right"); + REQUIRE(NAMEOF_ENUM(Directions::Left) == "Left"); + NAMEOF_DEBUG_REQUIRE(NAMEOF_ENUM(static_cast(0)).empty()); + + constexpr number nt = number::three; + constexpr auto nt_name = NAMEOF_ENUM(nt); + REQUIRE(NAMEOF_ENUM(number::one) == "one"); + REQUIRE(NAMEOF_ENUM(number::two) == "two"); + REQUIRE(nt_name == "three"); + NAMEOF_DEBUG_REQUIRE(NAMEOF_ENUM(number::four).empty()); + NAMEOF_DEBUG_REQUIRE(NAMEOF_ENUM(static_cast(0)).empty()); +} + +TEST_CASE("NAMEOF_ENUM_CONST") { + constexpr Color cr = Color::RED; + constexpr auto cr_name = NAMEOF_ENUM_CONST(cr); + constexpr Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; + REQUIRE(cr_name == "RED"); + REQUIRE(NAMEOF_ENUM_CONST(Color::BLUE) == "BLUE"); + REQUIRE(NAMEOF_ENUM_CONST(cm[1]) == "GREEN"); + + constexpr Numbers no = Numbers::one; + constexpr auto no_name = NAMEOF_ENUM_CONST(no); + REQUIRE(no_name == "one"); + REQUIRE(NAMEOF_ENUM_CONST(Numbers::two) == "two"); + REQUIRE(NAMEOF_ENUM_CONST(Numbers::three) == "three"); + REQUIRE(NAMEOF_ENUM_CONST(Numbers::many) == "many"); + + constexpr Directions dr = Directions::Right; + constexpr auto dr_name = NAMEOF_ENUM_CONST(dr); + REQUIRE(NAMEOF_ENUM_CONST(Directions::Up) == "Up"); + REQUIRE(NAMEOF_ENUM_CONST(Directions::Down) == "Down"); + REQUIRE(dr_name == "Right"); + REQUIRE(NAMEOF_ENUM_CONST(Directions::Left) == "Left"); + + constexpr number nt = number::three; + constexpr auto nt_name = NAMEOF_ENUM_CONST(nt); + REQUIRE(NAMEOF_ENUM_CONST(number::one) == "one"); + REQUIRE(NAMEOF_ENUM_CONST(number::two) == "two"); + REQUIRE(nt_name == "three"); + REQUIRE(NAMEOF_ENUM_CONST(number::four) == "four"); } TEST_CASE("NAMEOF_ENUM_FLAG") { @@ -411,11 +420,11 @@ TEST_CASE("NAMEOF_ENUM_FLAG") { REQUIRE(NAMEOF_ENUM_FLAG(afm[1]) == "CanFly"); REQUIRE(NAMEOF_ENUM_FLAG(AnimalFlags::EatsFish) == "EatsFish"); REQUIRE(NAMEOF_ENUM_FLAG(AnimalFlags::Endangered) == "Endangered"); - REQUIRE(NAMEOF_ENUM_FLAG(static_cast(0)).empty()); + NAMEOF_DEBUG_REQUIRE(NAMEOF_ENUM_FLAG(static_cast(0)).empty()); REQUIRE(NAMEOF_ENUM_FLAG(static_cast(1 | 2)) == "HasClaws|CanFly"); REQUIRE(NAMEOF_ENUM_FLAG(static_cast(1 | 2 | 4)) == "HasClaws|CanFly|EatsFish"); REQUIRE(NAMEOF_ENUM_FLAG(static_cast(1 | 0 | 8)) == "HasClaws|Endangered"); - REQUIRE(NAMEOF_ENUM_FLAG(static_cast(0)).empty()); + NAMEOF_DEBUG_REQUIRE(NAMEOF_ENUM_FLAG(static_cast(0)).empty()); constexpr BigFlags bf = BigFlags::A; auto bf_name = NAMEOF_ENUM_FLAG(bf); @@ -424,13 +433,13 @@ TEST_CASE("NAMEOF_ENUM_FLAG") { REQUIRE(NAMEOF_ENUM_FLAG(bfm[1]) == "B"); REQUIRE(NAMEOF_ENUM_FLAG(BigFlags::C) == "C"); REQUIRE(NAMEOF_ENUM_FLAG(BigFlags::D) == "D"); - REQUIRE(NAMEOF_ENUM_FLAG(static_cast(0)).empty()); - REQUIRE(NAMEOF_ENUM_FLAG(static_cast(1 | 2)).empty()); + NAMEOF_DEBUG_REQUIRE(NAMEOF_ENUM_FLAG(static_cast(0)).empty()); + NAMEOF_DEBUG_REQUIRE(NAMEOF_ENUM_FLAG(static_cast(1 | 2)).empty()); REQUIRE(NAMEOF_ENUM_FLAG(static_cast(1 | (static_cast(0x1) << 20))) == "A|B"); REQUIRE(NAMEOF_ENUM_FLAG(static_cast(1 | (static_cast(0x1) << 20) | (static_cast(0x1) << 63))) == "A|B|D"); REQUIRE(NAMEOF_ENUM_FLAG(static_cast((static_cast(0x1) << 63) | 1)) == "A|D"); - REQUIRE(NAMEOF_ENUM_FLAG(static_cast(2)).empty()); - REQUIRE(NAMEOF_ENUM_FLAG(static_cast((static_cast(0x1) << 63) | 2)).empty()); + NAMEOF_DEBUG_REQUIRE(NAMEOF_ENUM_FLAG(static_cast(2)).empty()); + NAMEOF_DEBUG_REQUIRE(NAMEOF_ENUM_FLAG(static_cast((static_cast(0x1) << 63) | 2)).empty()); } #endif @@ -439,106 +448,59 @@ TEST_CASE("NAMEOF_ENUM_FLAG") { static_assert(nameof::is_nameof_type_supported, "nameof::nameof_type: Unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); -TEST_CASE("NAMEOF_FULL_TYPE_EXPR") { - constexpr auto type_name = NAMEOF_FULL_TYPE_EXPR(struct_var); +TEST_CASE("nameof::nameof_type") { + constexpr auto type_name = nameof::nameof_type(); #if defined(__clang__) REQUIRE(type_name == "SomeStruct"); - REQUIRE(NAMEOF_FULL_TYPE_EXPR(ptr_s) == "SomeStruct *"); - REQUIRE(NAMEOF_FULL_TYPE_EXPR(ref_s) == "SomeStruct &"); + REQUIRE(nameof::nameof_type() == "SomeStruct *"); + REQUIRE(nameof::nameof_type() == "SomeStruct"); + REQUIRE(nameof::nameof_type() == "SomeStruct"); + REQUIRE(nameof::nameof_type() == "SomeStruct *"); + REQUIRE(nameof::nameof_type() == "SomeStruct"); + REQUIRE(nameof::nameof_type() == "const volatile SomeStruct *"); - REQUIRE(NAMEOF_FULL_TYPE_EXPR(ptr_c) == "const volatile SomeClass *"); + REQUIRE(nameof::nameof_type>() == "SomeClass"); + REQUIRE(nameof::nameof_type volatile *>() == "const volatile SomeClass *"); - REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar) == "Long"); - REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar.ll) == "Long::LL"); - REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar.ll.field) == "int"); + REQUIRE(nameof::nameof_type() == "Long"); + REQUIRE(nameof::nameof_type() == "Long"); + REQUIRE(nameof::nameof_type() == "Long::LL"); - REQUIRE(NAMEOF_FULL_TYPE_EXPR(Color::RED) == "Color"); - - REQUIRE(NAMEOF_FULL_TYPE_EXPR(std::declval>()) == "const SomeClass &&"); + REQUIRE(nameof::nameof_type() == "Color"); #elif defined(_MSC_VER) REQUIRE(type_name == "struct SomeStruct"); - REQUIRE(NAMEOF_FULL_TYPE_EXPR(ptr_s) == "struct SomeStruct *"); - REQUIRE(NAMEOF_FULL_TYPE_EXPR(ref_s) == "struct SomeStruct &"); + REQUIRE(nameof::nameof_type() == "struct SomeStruct *"); + REQUIRE(nameof::nameof_type() == "struct SomeStruct"); + REQUIRE(nameof::nameof_type() == "struct SomeStruct"); + REQUIRE(nameof::nameof_type() == "struct SomeStruct *"); + REQUIRE(nameof::nameof_type() == "struct SomeStruct"); + REQUIRE(nameof::nameof_type() == "struct SomeStruct const volatile *"); - REQUIRE(NAMEOF_FULL_TYPE_EXPR(ptr_c) == "class SomeClass const volatile *"); + REQUIRE(nameof::nameof_type>() == "class SomeClass"); + REQUIRE(nameof::nameof_type volatile *>() == "class SomeClass const volatile *"); - REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar) == "struct Long"); - REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar.ll) == "struct Long::LL"); - REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar.ll.field) == "int"); + REQUIRE(nameof::nameof_type() == "struct Long"); + REQUIRE(nameof::nameof_type() == "struct Long"); + REQUIRE(nameof::nameof_type() == "struct Long::LL"); - REQUIRE(NAMEOF_FULL_TYPE_EXPR(Color::RED) == "enum Color"); - - REQUIRE(NAMEOF_FULL_TYPE_EXPR(std::declval>()) == "class SomeClass const &&"); + REQUIRE(nameof::nameof_type() == "enum Color"); #elif defined(__GNUC__) REQUIRE(type_name == "SomeStruct"); - REQUIRE(NAMEOF_FULL_TYPE_EXPR(ptr_s) == "SomeStruct*"); - REQUIRE(NAMEOF_FULL_TYPE_EXPR(ref_s) == "SomeStruct&"); + REQUIRE(nameof::nameof_type() == "SomeStruct*"); + REQUIRE(nameof::nameof_type() == "SomeStruct"); + REQUIRE(nameof::nameof_type() == "SomeStruct"); + REQUIRE(nameof::nameof_type() == "SomeStruct*"); + REQUIRE(nameof::nameof_type() == "SomeStruct"); + REQUIRE(nameof::nameof_type() == "const volatile SomeStruct*"); - REQUIRE(NAMEOF_FULL_TYPE_EXPR(ptr_c) == "const volatile SomeClass*"); + REQUIRE(nameof::nameof_type>() == "SomeClass"); + REQUIRE(nameof::nameof_type volatile *>() == "const volatile SomeClass*"); - REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar) == "Long"); - REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar.ll) == "Long::LL"); - REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar.ll.field) == "int"); + REQUIRE(nameof::nameof_type() == "Long"); + REQUIRE(nameof::nameof_type() == "Long"); + REQUIRE(nameof::nameof_type() == "Long::LL"); - REQUIRE(NAMEOF_FULL_TYPE_EXPR(Color::RED) == "Color"); - - REQUIRE(NAMEOF_FULL_TYPE_EXPR(std::declval>()) == "const SomeClass&&"); -#endif -} - -TEST_CASE("NAMEOF_FULL_TYPE") { - constexpr auto type_name = NAMEOF_FULL_TYPE(decltype(struct_var)); -#if defined(__clang__) - REQUIRE(type_name == "SomeStruct"); - REQUIRE(NAMEOF_FULL_TYPE(decltype(ptr_s)) == "SomeStruct *"); - REQUIRE(NAMEOF_FULL_TYPE(decltype(ref_s)) == "SomeStruct &"); - REQUIRE(NAMEOF_FULL_TYPE(SomeStruct) == "SomeStruct"); - REQUIRE(NAMEOF_FULL_TYPE(SomeStruct *) == "SomeStruct *"); - REQUIRE(NAMEOF_FULL_TYPE(SomeStruct &) == "SomeStruct &"); - REQUIRE(NAMEOF_FULL_TYPE(const SomeStruct volatile *) == "const volatile SomeStruct *"); - - REQUIRE(NAMEOF_FULL_TYPE(SomeClass) == "SomeClass"); - REQUIRE(NAMEOF_FULL_TYPE(const SomeClass volatile *) == "const volatile SomeClass *"); - - REQUIRE(NAMEOF_FULL_TYPE(decltype(othervar)) == "Long"); - REQUIRE(NAMEOF_FULL_TYPE(Long) == "Long"); - REQUIRE(NAMEOF_FULL_TYPE(Long::LL) == "Long::LL"); - - REQUIRE(NAMEOF_FULL_TYPE(Color) == "Color"); -#elif defined(_MSC_VER) - REQUIRE(type_name == "struct SomeStruct"); - REQUIRE(NAMEOF_FULL_TYPE(decltype(ptr_s)) == "struct SomeStruct *"); - REQUIRE(NAMEOF_FULL_TYPE(decltype(ref_s)) == "struct SomeStruct &"); - REQUIRE(NAMEOF_FULL_TYPE(SomeStruct) == "struct SomeStruct"); - REQUIRE(NAMEOF_FULL_TYPE(SomeStruct *) == "struct SomeStruct *"); - REQUIRE(NAMEOF_FULL_TYPE(SomeStruct &) == "struct SomeStruct &"); - REQUIRE(NAMEOF_FULL_TYPE(const SomeStruct volatile *) == "struct SomeStruct const volatile *"); - - REQUIRE(NAMEOF_FULL_TYPE(SomeClass) == "class SomeClass"); - REQUIRE(NAMEOF_FULL_TYPE(const SomeClass volatile *) == "class SomeClass const volatile *"); - - REQUIRE(NAMEOF_FULL_TYPE(decltype(othervar)) == "struct Long"); - REQUIRE(NAMEOF_FULL_TYPE(Long) == "struct Long"); - REQUIRE(NAMEOF_FULL_TYPE(Long::LL) == "struct Long::LL"); - - REQUIRE(NAMEOF_FULL_TYPE(Color) == "enum Color"); -#elif defined(__GNUC__) - REQUIRE(type_name == "SomeStruct"); - REQUIRE(NAMEOF_FULL_TYPE(decltype(ptr_s)) == "SomeStruct*"); - REQUIRE(NAMEOF_FULL_TYPE(decltype(ref_s)) == "SomeStruct&"); - REQUIRE(NAMEOF_FULL_TYPE(SomeStruct) == "SomeStruct"); - REQUIRE(NAMEOF_FULL_TYPE(SomeStruct *) == "SomeStruct*"); - REQUIRE(NAMEOF_FULL_TYPE(SomeStruct &) == "SomeStruct&"); - REQUIRE(NAMEOF_FULL_TYPE(const SomeStruct volatile *) == "const volatile SomeStruct*"); - - REQUIRE(NAMEOF_FULL_TYPE(SomeClass) == "SomeClass"); - REQUIRE(NAMEOF_FULL_TYPE(const SomeClass volatile *) == "const volatile SomeClass*"); - - REQUIRE(NAMEOF_FULL_TYPE(decltype(othervar)) == "Long"); - REQUIRE(NAMEOF_FULL_TYPE(Long) == "Long"); - REQUIRE(NAMEOF_FULL_TYPE(Long::LL) == "Long::LL"); - - REQUIRE(NAMEOF_FULL_TYPE(Color) == "Color"); + REQUIRE(nameof::nameof_type() == "Color"); #endif } @@ -598,51 +560,22 @@ TEST_CASE("nameof::nameof_full_type") { #endif } -TEST_CASE("NAMEOF_TYPE_EXPR") { - constexpr auto type_name = NAMEOF_TYPE_EXPR(struct_var); -#if defined(__clang__) +TEST_CASE("nameof::nameof_short_type") { + constexpr auto type_name = nameof::nameof_short_type(); REQUIRE(type_name == "SomeStruct"); - REQUIRE(NAMEOF_TYPE_EXPR(ptr_s) == "SomeStruct *"); - REQUIRE(NAMEOF_TYPE_EXPR(ref_s) == "SomeStruct"); + REQUIRE(nameof::nameof_short_type() == "SomeStruct"); + REQUIRE(nameof::nameof_short_type() == "SomeStruct"); + REQUIRE(nameof::nameof_short_type() == "SomeStruct"); + REQUIRE(nameof::nameof_short_type() == "SomeStruct"); - REQUIRE(NAMEOF_TYPE_EXPR(ptr_c) == "const volatile SomeClass *"); + REQUIRE(nameof::nameof_short_type>() == "SomeClass"); + REQUIRE(nameof::nameof_short_type volatile>() == "SomeClass"); - REQUIRE(NAMEOF_TYPE_EXPR(othervar) == "Long"); - REQUIRE(NAMEOF_TYPE_EXPR(othervar.ll) == "Long::LL"); - REQUIRE(NAMEOF_TYPE_EXPR(othervar.ll.field) == "int"); + REQUIRE(nameof::nameof_short_type() == "Long"); + REQUIRE(nameof::nameof_short_type() == "Long"); + REQUIRE(nameof::nameof_short_type() == "LL"); - REQUIRE(NAMEOF_TYPE_EXPR(Color::RED) == "Color"); - - REQUIRE(NAMEOF_TYPE_EXPR(std::declval>()) == "SomeClass"); -#elif defined(_MSC_VER) - REQUIRE(type_name == "struct SomeStruct"); - REQUIRE(NAMEOF_TYPE_EXPR(ptr_s) == "struct SomeStruct *"); - REQUIRE(NAMEOF_TYPE_EXPR(ref_s) == "struct SomeStruct"); - - REQUIRE(NAMEOF_TYPE_EXPR(ptr_c) == "class SomeClass const volatile *"); - - REQUIRE(NAMEOF_TYPE_EXPR(othervar) == "struct Long"); - REQUIRE(NAMEOF_TYPE_EXPR(othervar.ll) == "struct Long::LL"); - REQUIRE(NAMEOF_TYPE_EXPR(othervar.ll.field) == "int"); - - REQUIRE(NAMEOF_TYPE_EXPR(Color::RED) == "enum Color"); - - REQUIRE(NAMEOF_TYPE_EXPR(std::declval>()) == "class SomeClass"); -#elif defined(__GNUC__) - REQUIRE(type_name == "SomeStruct"); - REQUIRE(NAMEOF_TYPE_EXPR(ptr_s) == "SomeStruct*"); - REQUIRE(NAMEOF_TYPE_EXPR(ref_s) == "SomeStruct"); - - REQUIRE(NAMEOF_TYPE_EXPR(ptr_c) == "const volatile SomeClass*"); - - REQUIRE(NAMEOF_TYPE_EXPR(othervar) == "Long"); - REQUIRE(NAMEOF_TYPE_EXPR(othervar.ll) == "Long::LL"); - REQUIRE(NAMEOF_TYPE_EXPR(othervar.ll.field) == "int"); - - REQUIRE(NAMEOF_TYPE_EXPR(Color::RED) == "Color"); - - REQUIRE(NAMEOF_TYPE_EXPR(std::declval>()) == "SomeClass"); -#endif + REQUIRE(nameof::nameof_short_type() == "Color"); } TEST_CASE("NAMEOF_TYPE") { @@ -701,72 +634,248 @@ TEST_CASE("NAMEOF_TYPE") { #endif } -TEST_CASE("nameof::nameof_type") { - constexpr auto type_name = nameof::nameof_type(); +TEST_CASE("NAMEOF_TYPE_EXPR") { + constexpr auto type_name = NAMEOF_TYPE_EXPR(struct_var); #if defined(__clang__) REQUIRE(type_name == "SomeStruct"); - REQUIRE(nameof::nameof_type() == "SomeStruct *"); - REQUIRE(nameof::nameof_type() == "SomeStruct"); - REQUIRE(nameof::nameof_type() == "SomeStruct"); - REQUIRE(nameof::nameof_type() == "SomeStruct *"); - REQUIRE(nameof::nameof_type() == "SomeStruct"); - REQUIRE(nameof::nameof_type() == "const volatile SomeStruct *"); + REQUIRE(NAMEOF_TYPE_EXPR(ptr_s) == "SomeStruct *"); + REQUIRE(NAMEOF_TYPE_EXPR(ref_s) == "SomeStruct"); - REQUIRE(nameof::nameof_type>() == "SomeClass"); - REQUIRE(nameof::nameof_type volatile *>() == "const volatile SomeClass *"); + REQUIRE(NAMEOF_TYPE_EXPR(ptr_c) == "const volatile SomeClass *"); - REQUIRE(nameof::nameof_type() == "Long"); - REQUIRE(nameof::nameof_type() == "Long"); - REQUIRE(nameof::nameof_type() == "Long::LL"); + REQUIRE(NAMEOF_TYPE_EXPR(othervar) == "Long"); + REQUIRE(NAMEOF_TYPE_EXPR(othervar.ll) == "Long::LL"); + REQUIRE(NAMEOF_TYPE_EXPR(othervar.ll.field) == "int"); - REQUIRE(nameof::nameof_type() == "Color"); + REQUIRE(NAMEOF_TYPE_EXPR(Color::RED) == "Color"); + + REQUIRE(NAMEOF_TYPE_EXPR(std::declval>()) == "SomeClass"); #elif defined(_MSC_VER) REQUIRE(type_name == "struct SomeStruct"); - REQUIRE(nameof::nameof_type() == "struct SomeStruct *"); - REQUIRE(nameof::nameof_type() == "struct SomeStruct"); - REQUIRE(nameof::nameof_type() == "struct SomeStruct"); - REQUIRE(nameof::nameof_type() == "struct SomeStruct *"); - REQUIRE(nameof::nameof_type() == "struct SomeStruct"); - REQUIRE(nameof::nameof_type() == "struct SomeStruct const volatile *"); + REQUIRE(NAMEOF_TYPE_EXPR(ptr_s) == "struct SomeStruct *"); + REQUIRE(NAMEOF_TYPE_EXPR(ref_s) == "struct SomeStruct"); - REQUIRE(nameof::nameof_type>() == "class SomeClass"); - REQUIRE(nameof::nameof_type volatile *>() == "class SomeClass const volatile *"); + REQUIRE(NAMEOF_TYPE_EXPR(ptr_c) == "class SomeClass const volatile *"); - REQUIRE(nameof::nameof_type() == "struct Long"); - REQUIRE(nameof::nameof_type() == "struct Long"); - REQUIRE(nameof::nameof_type() == "struct Long::LL"); + REQUIRE(NAMEOF_TYPE_EXPR(othervar) == "struct Long"); + REQUIRE(NAMEOF_TYPE_EXPR(othervar.ll) == "struct Long::LL"); + REQUIRE(NAMEOF_TYPE_EXPR(othervar.ll.field) == "int"); - REQUIRE(nameof::nameof_type() == "enum Color"); + REQUIRE(NAMEOF_TYPE_EXPR(Color::RED) == "enum Color"); + + REQUIRE(NAMEOF_TYPE_EXPR(std::declval>()) == "class SomeClass"); #elif defined(__GNUC__) REQUIRE(type_name == "SomeStruct"); - REQUIRE(nameof::nameof_type() == "SomeStruct*"); - REQUIRE(nameof::nameof_type() == "SomeStruct"); - REQUIRE(nameof::nameof_type() == "SomeStruct"); - REQUIRE(nameof::nameof_type() == "SomeStruct*"); - REQUIRE(nameof::nameof_type() == "SomeStruct"); - REQUIRE(nameof::nameof_type() == "const volatile SomeStruct*"); + REQUIRE(NAMEOF_TYPE_EXPR(ptr_s) == "SomeStruct*"); + REQUIRE(NAMEOF_TYPE_EXPR(ref_s) == "SomeStruct"); - REQUIRE(nameof::nameof_type>() == "SomeClass"); - REQUIRE(nameof::nameof_type volatile *>() == "const volatile SomeClass*"); + REQUIRE(NAMEOF_TYPE_EXPR(ptr_c) == "const volatile SomeClass*"); - REQUIRE(nameof::nameof_type() == "Long"); - REQUIRE(nameof::nameof_type() == "Long"); - REQUIRE(nameof::nameof_type() == "Long::LL"); + REQUIRE(NAMEOF_TYPE_EXPR(othervar) == "Long"); + REQUIRE(NAMEOF_TYPE_EXPR(othervar.ll) == "Long::LL"); + REQUIRE(NAMEOF_TYPE_EXPR(othervar.ll.field) == "int"); - REQUIRE(nameof::nameof_type() == "Color"); + REQUIRE(NAMEOF_TYPE_EXPR(Color::RED) == "Color"); + + REQUIRE(NAMEOF_TYPE_EXPR(std::declval>()) == "SomeClass"); #endif } +TEST_CASE("NAMEOF_FULL_TYPE") { + constexpr auto type_name = NAMEOF_FULL_TYPE(decltype(struct_var)); +#if defined(__clang__) + REQUIRE(type_name == "SomeStruct"); + REQUIRE(NAMEOF_FULL_TYPE(decltype(ptr_s)) == "SomeStruct *"); + REQUIRE(NAMEOF_FULL_TYPE(decltype(ref_s)) == "SomeStruct &"); + REQUIRE(NAMEOF_FULL_TYPE(SomeStruct) == "SomeStruct"); + REQUIRE(NAMEOF_FULL_TYPE(SomeStruct *) == "SomeStruct *"); + REQUIRE(NAMEOF_FULL_TYPE(SomeStruct &) == "SomeStruct &"); + REQUIRE(NAMEOF_FULL_TYPE(const SomeStruct volatile *) == "const volatile SomeStruct *"); + + REQUIRE(NAMEOF_FULL_TYPE(SomeClass) == "SomeClass"); + REQUIRE(NAMEOF_FULL_TYPE(const SomeClass volatile *) == "const volatile SomeClass *"); + + REQUIRE(NAMEOF_FULL_TYPE(decltype(othervar)) == "Long"); + REQUIRE(NAMEOF_FULL_TYPE(Long) == "Long"); + REQUIRE(NAMEOF_FULL_TYPE(Long::LL) == "Long::LL"); + + REQUIRE(NAMEOF_FULL_TYPE(Color) == "Color"); +#elif defined(_MSC_VER) + REQUIRE(type_name == "struct SomeStruct"); + REQUIRE(NAMEOF_FULL_TYPE(decltype(ptr_s)) == "struct SomeStruct *"); + REQUIRE(NAMEOF_FULL_TYPE(decltype(ref_s)) == "struct SomeStruct &"); + REQUIRE(NAMEOF_FULL_TYPE(SomeStruct) == "struct SomeStruct"); + REQUIRE(NAMEOF_FULL_TYPE(SomeStruct *) == "struct SomeStruct *"); + REQUIRE(NAMEOF_FULL_TYPE(SomeStruct &) == "struct SomeStruct &"); + REQUIRE(NAMEOF_FULL_TYPE(const SomeStruct volatile *) == "struct SomeStruct const volatile *"); + + REQUIRE(NAMEOF_FULL_TYPE(SomeClass) == "class SomeClass"); + REQUIRE(NAMEOF_FULL_TYPE(const SomeClass volatile *) == "class SomeClass const volatile *"); + + REQUIRE(NAMEOF_FULL_TYPE(decltype(othervar)) == "struct Long"); + REQUIRE(NAMEOF_FULL_TYPE(Long) == "struct Long"); + REQUIRE(NAMEOF_FULL_TYPE(Long::LL) == "struct Long::LL"); + + REQUIRE(NAMEOF_FULL_TYPE(Color) == "enum Color"); +#elif defined(__GNUC__) + REQUIRE(type_name == "SomeStruct"); + REQUIRE(NAMEOF_FULL_TYPE(decltype(ptr_s)) == "SomeStruct*"); + REQUIRE(NAMEOF_FULL_TYPE(decltype(ref_s)) == "SomeStruct&"); + REQUIRE(NAMEOF_FULL_TYPE(SomeStruct) == "SomeStruct"); + REQUIRE(NAMEOF_FULL_TYPE(SomeStruct *) == "SomeStruct*"); + REQUIRE(NAMEOF_FULL_TYPE(SomeStruct &) == "SomeStruct&"); + REQUIRE(NAMEOF_FULL_TYPE(const SomeStruct volatile *) == "const volatile SomeStruct*"); + + REQUIRE(NAMEOF_FULL_TYPE(SomeClass) == "SomeClass"); + REQUIRE(NAMEOF_FULL_TYPE(const SomeClass volatile *) == "const volatile SomeClass*"); + + REQUIRE(NAMEOF_FULL_TYPE(decltype(othervar)) == "Long"); + REQUIRE(NAMEOF_FULL_TYPE(Long) == "Long"); + REQUIRE(NAMEOF_FULL_TYPE(Long::LL) == "Long::LL"); + + REQUIRE(NAMEOF_FULL_TYPE(Color) == "Color"); +#endif +} + +TEST_CASE("NAMEOF_FULL_TYPE_EXPR") { + constexpr auto type_name = NAMEOF_FULL_TYPE_EXPR(struct_var); +#if defined(__clang__) + REQUIRE(type_name == "SomeStruct"); + REQUIRE(NAMEOF_FULL_TYPE_EXPR(ptr_s) == "SomeStruct *"); + REQUIRE(NAMEOF_FULL_TYPE_EXPR(ref_s) == "SomeStruct &"); + + REQUIRE(NAMEOF_FULL_TYPE_EXPR(ptr_c) == "const volatile SomeClass *"); + + REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar) == "Long"); + REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar.ll) == "Long::LL"); + REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar.ll.field) == "int"); + + REQUIRE(NAMEOF_FULL_TYPE_EXPR(Color::RED) == "Color"); + + REQUIRE(NAMEOF_FULL_TYPE_EXPR(std::declval>()) == "const SomeClass &&"); +#elif defined(_MSC_VER) + REQUIRE(type_name == "struct SomeStruct"); + REQUIRE(NAMEOF_FULL_TYPE_EXPR(ptr_s) == "struct SomeStruct *"); + REQUIRE(NAMEOF_FULL_TYPE_EXPR(ref_s) == "struct SomeStruct &"); + + REQUIRE(NAMEOF_FULL_TYPE_EXPR(ptr_c) == "class SomeClass const volatile *"); + + REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar) == "struct Long"); + REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar.ll) == "struct Long::LL"); + REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar.ll.field) == "int"); + + REQUIRE(NAMEOF_FULL_TYPE_EXPR(Color::RED) == "enum Color"); + + REQUIRE(NAMEOF_FULL_TYPE_EXPR(std::declval>()) == "class SomeClass const &&"); +#elif defined(__GNUC__) + REQUIRE(type_name == "SomeStruct"); + REQUIRE(NAMEOF_FULL_TYPE_EXPR(ptr_s) == "SomeStruct*"); + REQUIRE(NAMEOF_FULL_TYPE_EXPR(ref_s) == "SomeStruct&"); + + REQUIRE(NAMEOF_FULL_TYPE_EXPR(ptr_c) == "const volatile SomeClass*"); + + REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar) == "Long"); + REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar.ll) == "Long::LL"); + REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar.ll.field) == "int"); + + REQUIRE(NAMEOF_FULL_TYPE_EXPR(Color::RED) == "Color"); + + REQUIRE(NAMEOF_FULL_TYPE_EXPR(std::declval>()) == "const SomeClass&&"); +#endif +} + +TEST_CASE("NAMEOF_SHORT_TYPE") { + constexpr auto type_name = NAMEOF_SHORT_TYPE(decltype(struct_var)); + REQUIRE(type_name == "SomeStruct"); + REQUIRE(NAMEOF_SHORT_TYPE(decltype(ref_s)) == "SomeStruct"); + REQUIRE(NAMEOF_SHORT_TYPE(SomeStruct) == "SomeStruct"); + REQUIRE(NAMEOF_SHORT_TYPE(SomeStruct &) == "SomeStruct"); + REQUIRE(NAMEOF_SHORT_TYPE(const SomeStruct volatile) == "SomeStruct"); + + REQUIRE(NAMEOF_SHORT_TYPE(SomeClass) == "SomeClass"); + REQUIRE(NAMEOF_SHORT_TYPE(const SomeClass volatile) == "SomeClass"); + + REQUIRE(NAMEOF_SHORT_TYPE(decltype(othervar)) == "Long"); + REQUIRE(NAMEOF_SHORT_TYPE(Long) == "Long"); + REQUIRE(NAMEOF_SHORT_TYPE(Long::LL) == "LL"); + + REQUIRE(NAMEOF_SHORT_TYPE(Color) == "Color"); +} + +TEST_CASE("NAMEOF_SHORT_TYPE_EXPR") { + constexpr auto type_name = NAMEOF_SHORT_TYPE_EXPR(struct_var); + REQUIRE(type_name == "SomeStruct"); + REQUIRE(NAMEOF_SHORT_TYPE_EXPR(ref_s) == "SomeStruct"); + + REQUIRE(NAMEOF_SHORT_TYPE_EXPR(othervar) == "Long"); + REQUIRE(NAMEOF_SHORT_TYPE_EXPR(othervar.ll) == "LL"); + REQUIRE(NAMEOF_SHORT_TYPE_EXPR(othervar.ll.field) == "int"); + + REQUIRE(NAMEOF_SHORT_TYPE_EXPR(Color::RED) == "Color"); + + REQUIRE(NAMEOF_SHORT_TYPE_EXPR(std::declval>()) == "SomeClass"); +} + #endif #if defined(NAMEOF_TYPE_RTTI_SUPPORTED) && NAMEOF_TYPE_RTTI_SUPPORTED + TEST_CASE("NAMEOF_TYPE_RTTI") { -#if defined(__clang__) - REQUIRE(NAMEOF_TYPE_RTTI(Color) == "Color"); + TestRtti::Base* ptr = new TestRtti::Derived(); + const TestRtti::Base& const_ref = *ptr; + volatile TestRtti::Base& volatile_ref = *ptr; + volatile const TestRtti::Base& cv_ref = *ptr; +#if defined(__clang__) && !defined(_MSC_VER) + REQUIRE(NAMEOF_TYPE_RTTI(*ptr) == "TestRtti::Derived"); + REQUIRE(NAMEOF_TYPE_RTTI(const_ref) == "TestRtti::Derived"); + REQUIRE(NAMEOF_TYPE_RTTI(volatile_ref) == "TestRtti::Derived"); + REQUIRE(NAMEOF_TYPE_RTTI(cv_ref) == "TestRtti::Derived"); #elif defined(_MSC_VER) - REQUIRE(NAMEOF_TYPE_RTTI(Color) == "enum Color"); + REQUIRE(NAMEOF_TYPE_RTTI(*ptr) == "struct TestRtti::Derived"); + REQUIRE(NAMEOF_TYPE_RTTI(const_ref) == "struct TestRtti::Derived"); + REQUIRE(NAMEOF_TYPE_RTTI(volatile_ref) == "struct TestRtti::Derived"); + REQUIRE(NAMEOF_TYPE_RTTI(cv_ref) == "struct TestRtti::Derived"); #elif defined(__GNUC__) - REQUIRE(NAMEOF_TYPE_RTTI(Color) == "Color"); + REQUIRE(NAMEOF_TYPE_RTTI(*ptr) == "TestRtti::Derived"); + REQUIRE(NAMEOF_TYPE_RTTI(const_ref) == "TestRtti::Derived"); + REQUIRE(NAMEOF_TYPE_RTTI(volatile_ref) == "TestRtti::Derived"); + REQUIRE(NAMEOF_TYPE_RTTI(cv_ref) == "TestRtti::Derived"); #endif } + +TEST_CASE("NAMEOF_FULL_TYPE_RTTI") { + TestRtti::Base* ptr = new TestRtti::Derived(); + const TestRtti::Base& const_ref = *ptr; + volatile TestRtti::Base& volatile_ref = *ptr; + volatile const TestRtti::Base& cv_ref = *ptr; +#if defined(__clang__) && !defined(_MSC_VER) + REQUIRE(NAMEOF_FULL_TYPE_RTTI(*ptr) == "TestRtti::Derived&"); + REQUIRE(NAMEOF_FULL_TYPE_RTTI(const_ref) == "const TestRtti::Derived&"); + REQUIRE(NAMEOF_FULL_TYPE_RTTI(volatile_ref) == "volatile TestRtti::Derived&"); + REQUIRE(NAMEOF_FULL_TYPE_RTTI(cv_ref) == "volatile const TestRtti::Derived&"); +#elif defined(_MSC_VER) + REQUIRE(NAMEOF_FULL_TYPE_RTTI(*ptr) == "struct TestRtti::Derived&"); + REQUIRE(NAMEOF_FULL_TYPE_RTTI(const_ref) == "const struct TestRtti::Derived&"); + REQUIRE(NAMEOF_FULL_TYPE_RTTI(volatile_ref) == "volatile struct TestRtti::Derived&"); + REQUIRE(NAMEOF_FULL_TYPE_RTTI(cv_ref) == "volatile const struct TestRtti::Derived&"); +#elif defined(__GNUC__) + REQUIRE(NAMEOF_FULL_TYPE_RTTI(*ptr) == "TestRtti::Derived&"); + REQUIRE(NAMEOF_FULL_TYPE_RTTI(const_ref) == "const TestRtti::Derived&"); + REQUIRE(NAMEOF_FULL_TYPE_RTTI(volatile_ref) == "volatile TestRtti::Derived&"); + REQUIRE(NAMEOF_FULL_TYPE_RTTI(cv_ref) == "volatile const TestRtti::Derived&"); +#endif +} + +TEST_CASE("NAMEOF_SHORT_TYPE_RTTI") { + TestRtti::Base* ptr = new TestRtti::Derived(); + const TestRtti::Base& const_ref = *ptr; + volatile TestRtti::Base& volatile_ref = *ptr; + volatile const TestRtti::Base& cv_ref = *ptr; + + REQUIRE(NAMEOF_SHORT_TYPE_RTTI(*ptr) == "Derived"); + REQUIRE(NAMEOF_SHORT_TYPE_RTTI(const_ref) == "Derived"); + REQUIRE(NAMEOF_SHORT_TYPE_RTTI(volatile_ref) == "Derived"); + REQUIRE(NAMEOF_SHORT_TYPE_RTTI(cv_ref) == "Derived"); +} + #endif