├── .github └── workflows │ ├── macos.yml │ ├── ubuntu.yml │ └── windows.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake └── GenPkgConfig │ ├── GenPkgConfig.cmake │ ├── ReadMe.md │ ├── UNLICENSE │ └── buildTimeScripts │ └── getObjectFilesBaseNames.cmake ├── doc ├── limitations.md └── reference.md ├── example ├── CMakeLists.txt ├── example.cpp └── example_custom_name.cpp ├── include └── nameof.hpp └── test ├── 3rdparty └── Catch2 │ ├── LICENSE │ └── catch.hpp ├── CMakeLists.txt ├── test.cpp └── test_aliases.cpp /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | name: macos 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: read-all 6 | 7 | jobs: 8 | build: 9 | runs-on: ${{ matrix.config.os }} 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | config: 14 | - { os: macos-13 } # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-13-Readme.md#xcode 15 | - { os: macos-14 } # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-14-Readme.md#xcode 16 | 17 | name: "${{ matrix.config.os }}" 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - name: Build Release 22 | run: | 23 | rm -rf build 24 | mkdir build 25 | cd build 26 | cmake .. -DCMAKE_BUILD_TYPE=Release 27 | cmake --build . -j 4 --config Release 28 | ctest --output-on-failure -C Release 29 | 30 | - name: Build Debug 31 | run: | 32 | rm -rf build 33 | mkdir build 34 | cd build 35 | cmake .. -DCMAKE_BUILD_TYPE=Debug 36 | cmake --build . -j 4 --config Debug 37 | ctest --output-on-failure -C Debug 38 | -------------------------------------------------------------------------------- /.github/workflows/ubuntu.yml: -------------------------------------------------------------------------------- 1 | name: ubuntu 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: read-all 6 | 7 | jobs: 8 | ubuntu: 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | compiler: 13 | - { cc: "gcc-9", cxx: "g++-9", os: "ubuntu-20.04" } 14 | - { cc: "gcc-10", cxx: "g++-10", os: "ubuntu-20.04" } 15 | - { cc: "gcc-10", cxx: "g++-10", os: "ubuntu-20.04" } 16 | - { cc: "gcc-11", cxx: "g++-11", os: "ubuntu-20.04" } 17 | - { cc: "gcc-11", cxx: "g++-11", os: "ubuntu-20.04" } 18 | - { cc: "gcc-12", cxx: "g++-12", os: "ubuntu-22.04" } 19 | - { cc: "gcc-13", cxx: "g++-12", os: "ubuntu-22.04" } 20 | - { cc: "gcc-14", cxx: "g++-12", os: "ubuntu-22.04" } 21 | - { cc: "clang-9", cxx: "clang++-9", os: "ubuntu-20.04" } 22 | - { cc: "clang-10", cxx: "clang++-10", os: "ubuntu-20.04" } 23 | - { cc: "clang-11", cxx: "clang++-11", os: "ubuntu-20.04" } 24 | - { cc: "clang-12", cxx: "clang++-12", os: "ubuntu-20.04" } 25 | - { cc: "clang-13", cxx: "clang++-13", os: "ubuntu-20.04" } 26 | - { cc: "clang-14", cxx: "clang++-14", os: "ubuntu-20.04" } 27 | - { cc: "clang-15", cxx: "clang++-15", os: "ubuntu-20.04" } 28 | - { cc: "clang-16", cxx: "clang++-16", os: "ubuntu-20.04" } 29 | 30 | name: "${{ matrix.compiler.cc }}" 31 | runs-on: ${{ matrix.compiler.os }} 32 | steps: 33 | - uses: actions/checkout@v4 34 | 35 | - name: Configure clang 36 | run: | 37 | if [[ "${{ matrix.compiler.cc }}" == "clang"* ]]; then 38 | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add - 39 | sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-9 main" 40 | sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-10 main" 41 | sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-11 main" 42 | sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-12 main" 43 | sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-13 main" 44 | sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-14 main" 45 | sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-15 main" 46 | sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-16 main" 47 | sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main" 48 | sudo apt update 49 | sudo apt install ${{ matrix.compiler.cc }} -y 50 | fi 51 | 52 | - name: Configure gcc 53 | run: | 54 | if [[ "${{ matrix.compiler.cc }}" == "gcc"* ]]; then 55 | sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y 56 | sudo apt update 57 | sudo apt install ${{ matrix.compiler.cxx }} -y 58 | fi 59 | 60 | - name: Build Release 61 | run: | 62 | rm -rf build 63 | mkdir build 64 | cd build 65 | cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cxx }} 66 | cmake --build . -j 4 --config Release 67 | ctest --output-on-failure -C Release 68 | 69 | - name: Build Debug 70 | run: | 71 | rm -rf build 72 | mkdir build 73 | cd build 74 | cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cxx }} 75 | cmake --build . -j 4 --config Debug 76 | ctest --output-on-failure -C Debug 77 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | name: windows 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: read-all 6 | 7 | jobs: 8 | build: 9 | runs-on: ${{ matrix.config.os }} 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | config: 14 | - { os: windows-2019, vs: "Visual Studio 2019" } # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md#visual-studio-enterprise-2019 15 | - { os: windows-2022, vs: "Visual Studio 2022" } # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2022-Readme.md#visual-studio-enterprise-2022 16 | 17 | name: "${{ matrix.config.vs }}" 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - name: Build Win32 22 | shell: bash 23 | run: | 24 | rm -rf build 25 | mkdir build 26 | cd build 27 | cmake .. -A Win32 28 | cmake --build . -j 4 --config Release 29 | ctest --output-on-failure -C Release 30 | cmake --build . -j 4 --config Debug 31 | ctest --output-on-failure -C Debug 32 | 33 | - name: Build x64 34 | shell: bash 35 | run: | 36 | rm -rf build 37 | mkdir build 38 | cd build 39 | cmake .. -A x64 40 | cmake --build . -j 4 --config Release 41 | ctest --output-on-failure -C Release 42 | cmake --build . -j 4 --config Debug 43 | ctest --output-on-failure -C Debug 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .vscode/ 3 | .vs/ 4 | 5 | ### C++ gitignore ### 6 | # Prerequisites 7 | *.d 8 | 9 | # Compiled Object files 10 | *.slo 11 | *.lo 12 | *.o 13 | *.obj 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Compiled Dynamic libraries 20 | *.so 21 | *.dylib 22 | *.dll 23 | 24 | # Fortran module files 25 | *.mod 26 | *.smod 27 | 28 | # Compiled Static libraries 29 | *.lai 30 | *.la 31 | *.a 32 | *.lib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | 39 | ### CMake gitignore ### 40 | CMakeLists.txt.user 41 | CMakeCache.txt 42 | CMakeFiles 43 | CMakeScripts 44 | Testing 45 | Makefile 46 | cmake_install.cmake 47 | install_manifest.txt 48 | compile_commands.json 49 | CTestTestfile.cmake 50 | _deps 51 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | include(GNUInstallDirs) 4 | 5 | set(ADDITIONAL_MODULES_DIR "${CMAKE_CURRENT_LIST_DIR}/cmake") 6 | list(APPEND CMAKE_MODULE_PATH "${ADDITIONAL_MODULES_DIR}") 7 | 8 | project(nameof 9 | VERSION "0.10.4" 10 | HOMEPAGE_URL "https://github.com/Neargye/nameof" 11 | DESCRIPTION "A library that provides nameof macros and functions to simply obtain the name of a variable, type, function, macro, and enum." 12 | LANGUAGES CXX 13 | ) 14 | set(CPACK_PACKAGE_VENDOR "Daniil Goncharov") 15 | 16 | if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) 17 | set(IS_TOPLEVEL_PROJECT TRUE) 18 | else() 19 | set(IS_TOPLEVEL_PROJECT FALSE) 20 | endif() 21 | 22 | option(NAMEOF_OPT_BUILD_EXAMPLES "Build nameof examples" ${IS_TOPLEVEL_PROJECT}) 23 | option(NAMEOF_OPT_BUILD_TESTS "Build and perform nameof tests" ${IS_TOPLEVEL_PROJECT}) 24 | option(NAMEOF_OPT_INSTALL "Generate and install nameof target" ${IS_TOPLEVEL_PROJECT}) 25 | 26 | if(NAMEOF_OPT_BUILD_EXAMPLES) 27 | add_subdirectory(example) 28 | endif() 29 | 30 | if(NAMEOF_OPT_BUILD_TESTS) 31 | enable_testing() 32 | add_subdirectory(test) 33 | endif() 34 | 35 | include(CMakePackageConfigHelpers) 36 | 37 | set(EXPORT_NAMESPACE "${PROJECT_NAME}::") 38 | 39 | add_library("${PROJECT_NAME}" INTERFACE) 40 | add_library("${EXPORT_NAMESPACE}${PROJECT_NAME}" ALIAS "${PROJECT_NAME}") 41 | set(INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/include") 42 | 43 | target_include_directories(${PROJECT_NAME} 44 | INTERFACE 45 | $ 46 | $) 47 | 48 | if(NAMEOF_OPT_INSTALL) 49 | list(APPEND CMAKE_MODULE_PATH "${ADDITIONAL_MODULES_DIR}/GenPkgConfig") 50 | include(GenPkgConfig) 51 | include(CPackComponent) 52 | include(CMakePackageConfigHelpers) 53 | 54 | string(REPLACE "/${CMAKE_LIBRARY_ARCHITECTURE}" "" CMAKE_INSTALL_LIBDIR_ARCHIND "${CMAKE_INSTALL_LIBDIR}") 55 | install(TARGETS "${PROJECT_NAME}" 56 | EXPORT ${PROJECT_NAME} 57 | INCLUDES 58 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" 59 | # COMPONENT "${SDK_COMPONENT_NAME}" # component is not allowed for includes! Headers are installed separately! Includes only marks the headers for export 60 | ) 61 | 62 | file(GLOB_RECURSE HEADERS "${INCLUDES}/*.h" "${INCLUDES}/*.hxx" "${INCLUDES}/*.hpp") 63 | foreach(headerFile ${HEADERS}) 64 | get_filename_component(headerFileParentDir "${headerFile}" DIRECTORY) 65 | file(RELATIVE_PATH headerFileRelParentDir "${INCLUDES}" "${headerFileParentDir}") 66 | 67 | install(FILES "${headerFile}" 68 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${headerFileRelParentDir}" 69 | ) 70 | endforeach() 71 | 72 | set(CPACK_PACKAGE_NAME "${PROJECT_NAME}") 73 | set(CPACK_PACKAGE_DESCRIPTION "${PROJECT_DESCRIPTION}") 74 | set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "all") 75 | set(CPACK_DEBIAN_PACKAGE_NAME "lib${CPACK_PACKAGE_NAME}-dev") 76 | set(CPACK_RPM_PACKAGE_NAME "lib${CPACK_PACKAGE_NAME}-devel") 77 | set(CPACK_PACKAGE_HOMEPAGE_URL "${PROJECT_HOMEPAGE_URL}") 78 | set(CPACK_PACKAGE_MAINTAINER "${CPACK_PACKAGE_VENDOR}") 79 | set(CPACK_DEBIAN_PACKAGE_DEPENDS "") 80 | set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${CPACK_PACKAGE_MAINTAINER}") 81 | set(CPACK_PACKAGE_MAINTAINER "${CPACK_PACKAGE_VENDOR}") 82 | set(CPACK_DEB_COMPONENT_INSTALL ON) 83 | set(CPACK_RPM_COMPONENT_INSTALL ON) 84 | set(CPACK_NSIS_COMPONENT_INSTALL ON) 85 | set(CPACK_DEBIAN_COMPRESSION_TYPE "xz") 86 | 87 | set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") 88 | set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md") 89 | 90 | set(CMAKE_CONFIG_FILE_BASENAME "${PROJECT_NAME}Config.cmake") 91 | set(CMAKE_EXPORT_FILE_BASENAME "${PROJECT_NAME}Export.cmake") 92 | set(CMAKE_CONFIG_VERSION_FILE_BASENAME "${PROJECT_NAME}ConfigVersion.cmake") 93 | set(CMAKE_CONFIG_VERSION_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CONFIG_VERSION_FILE_BASENAME}") 94 | 95 | export(TARGETS "${PROJECT_NAME}" 96 | NAMESPACE "${EXPORT_NAMESPACE}" 97 | FILE "${CMAKE_EXPORT_FILE_BASENAME}" 98 | EXPORT_LINK_INTERFACE_LIBRARIES 99 | ) 100 | 101 | install(EXPORT "${PROJECT_NAME}" 102 | FILE "${CMAKE_CONFIG_FILE_BASENAME}" 103 | NAMESPACE "${EXPORT_NAMESPACE}" 104 | DESTINATION "${CMAKE_INSTALL_LIBDIR_ARCHIND}/cmake/${PROJECT_NAME}" 105 | ) 106 | 107 | write_basic_package_version_file( 108 | "${CMAKE_CONFIG_VERSION_FILE_NAME}" 109 | #VERSION "100500.100500.100500" # any version of same bitness suits. CMake cannot compare to infinity, so use a large number we expect to be greater than any future version 110 | VERSION ${_VERSION} 111 | COMPATIBILITY AnyNewerVersion 112 | ARCH_INDEPENDENT 113 | ) 114 | install(FILES "${CMAKE_CONFIG_VERSION_FILE_NAME}" 115 | DESTINATION "${CMAKE_INSTALL_LIBDIR_ARCHIND}/cmake/${PROJECT_NAME}" 116 | ) 117 | 118 | configure_pkg_config_file("${PROJECT_NAME}" 119 | NAME "${PROJECT_NAME}" 120 | VERSION "${PROJECT_VERSION}" 121 | DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION}" 122 | URL "${CPACK_PACKAGE_HOMEPAGE_URL}" 123 | INSTALL_LIB_DIR "${CMAKE_INSTALL_LIBDIR_ARCHIND}" 124 | INSTALL_INCLUDE_DIR "${CMAKE_INSTALL_INCLUDEDIR}" 125 | ) 126 | 127 | include(CPack) 128 | endif() 129 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 - 2024 Daniil Goncharov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Github releases](https://img.shields.io/github/release/Neargye/nameof.svg)](https://github.com/Neargye/nameof/releases) 2 | [![Conan package](https://img.shields.io/badge/Conan-package-blueviolet)](https://conan.io/center/recipes/nameof) 3 | [![Vcpkg package](https://img.shields.io/badge/Vcpkg-package-blueviolet)](https://github.com/microsoft/vcpkg/tree/master/ports/nameof) 4 | [![License](https://img.shields.io/github/license/Neargye/nameof.svg)](LICENSE) 5 | [![Compiler explorer](https://img.shields.io/badge/compiler_explorer-online-blue.svg)](https://godbolt.org/z/s_ecko) 6 | [![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://stand-with-ukraine.pp.ua) 7 | 8 | # Nameof C++ 9 | 10 | Header-only C++17 library provides nameof macros and functions to simply obtain the name of a variable, type, function, macro, and enum. 11 | 12 | If you like this project, please consider donating to one of the funds that help victims of the war in Ukraine: https://u24.gov.ua. 13 | 14 | ## Documentation 15 | 16 | * [Reference](doc/reference.md) 17 | * [Limitations](doc/limitations.md) 18 | * [Integration](#Integration) 19 | 20 | ## [Features & Examples](example/example.cpp) 21 | 22 | * Nameof 23 | 24 | ```cpp 25 | // Name of variable. 26 | NAMEOF(somevar) -> "somevar" 27 | 28 | // Name of member variable. 29 | NAMEOF(person.address.zip_code) -> "zip_code" 30 | 31 | // Name of function. 32 | NAMEOF(foo()) -> "foo" 33 | 34 | // Name of member function. 35 | NAMEOF(somevar.some_method()) -> "some_method" 36 | NAMEOF(somevar.some_method()) -> "some_method" 37 | 38 | // Name of macro. 39 | NAMEOF(__LINE__) -> "__LINE__" 40 | NAMEOF(NAMEOF(structvar)) -> "NAMEOF" 41 | 42 | // Obtains full name of variable, function, macro. 43 | NAMEOF_FULL(somevar.some_method()) -> "some_method" 44 | 45 | // Obtains raw name of variable, function, macro. 46 | NAMEOF_RAW(somevar.some_method()) -> "somevar.some_method()" 47 | ``` 48 | 49 | * Nameof enum 50 | 51 | ```cpp 52 | enum class Color { RED = 1, BLUE = 2, GREEN = 4 }; 53 | 54 | auto color = Color::RED; 55 | // Name of enum variable. 56 | NAMEOF_ENUM(color) -> "RED" 57 | nameof::nameof_enum(color) -> "RED" 58 | 59 | // Static storage enum variable to string. 60 | // This version is much lighter on the compile times and is not restricted to the enum_range limitation. 61 | NAMEOF_ENUM_CONST(Color::GREEN) -> "GREEN" 62 | nameof::nameof_enum() -> "GREEN" 63 | 64 | // Enum flags variable to string. 65 | NAMEOF_ENUM_FLAG(Color::GREEN | Color::BLUE) -> "GREEN|BLUE" 66 | nameof::nameof_enum_flag() -> "GREEN|BLUE" 67 | 68 | // Obtains name of enum variable or default value if enum variable out of range. 69 | NAMEOF_ENUM_OR(Color::GREEN) -> "GREEN" 70 | NAMEOF_ENUM_OR((Color)0, "none") -> "none" 71 | ``` 72 | 73 | * Nameof type 74 | 75 | ```cpp 76 | const my::detail::SomeClass& var_ref = var; 77 | // Name of variable type. 78 | NAMEOF_TYPE_EXPR(var_ref) -> "my::detail::SomeClass" 79 | nameof::nameof_type() -> "my::detail::SomeClass" 80 | NAMEOF_FULL_TYPE_EXPR(var_ref) -> "const my::detail::SomeClass&" 81 | nameof::nameof_full_type() -> "const my::detail::SomeClass&" 82 | NAMEOF_SHORT_TYPE_EXPR(var_ref) -> "SomeClass" 83 | nameof::nameof_short_type() -> "SomeClass" 84 | 85 | using T = const my::detail::SomeClass&; 86 | // Name of type. 87 | NAMEOF_TYPE(T) ->"my::detail::SomeClass" 88 | nameof::nameof_type() -> "my::detail::SomeClass" 89 | NAMEOF_FULL_TYPE(T) -> "const my::detail::SomeClass&" 90 | nameof::nameof_full_type() -> "const my::detail::SomeClass&" 91 | NAMEOF_SHORT_TYPE(T) -> "SomeClass" 92 | nameof::nameof_short_type() -> "SomeClass" 93 | 94 | my::detail::Base* ptr = new my::detail::Derived(); 95 | // Name of type, using rtti. 96 | NAMEOF_TYPE_RTTI(*ptr) -> "my::detail::Derived" 97 | NAMEOF_FULL_TYPE_RTTI(*ptr) -> "volatile const my::detail::Derived&" 98 | NAMEOF_SHORT_TYPE_RTTI(*ptr) -> "Derived" 99 | 100 | struct A { 101 | int this_is_the_name; 102 | }; 103 | // Obtains name of member. 104 | NAMEOF_MEMBER(&A::this_is_the_name) -> "this_is_the_name" 105 | nameof::nameof_member(&A::this_is_the_name) -> "this_is_the_name" 106 | 107 | int someglobalvariable = 0; 108 | // Obtains name of a function, a global or class static variable. 109 | NAMEOF_POINTER(&someglobalconstvariable) == "someglobalconstvariable" 110 | nameof::nameof_pointer(&someglobalconstvariable) == "someglobalconstvariable" 111 | 112 | constexpr auto global_ptr = &someglobalvariable; 113 | NAMEOF_POINTER(global_ptr) == "someglobalconstvariable" 114 | nameof::nameof_pointer(global_ptr) == "someglobalconstvariable" 115 | ``` 116 | 117 | ## Remarks 118 | 119 | * Before use, read the [limitations](doc/limitations.md) of functionality. 120 | 121 | ## Integration 122 | 123 | You should add required file [nameof.hpp](include/nameof.hpp). 124 | 125 | If you are using [vcpkg](https://github.com/Microsoft/vcpkg/) on your project for external dependencies, then you can use the [nameof package](https://github.com/microsoft/vcpkg/tree/master/ports/nameof). 126 | 127 | If you are using [Conan](https://www.conan.io/) to manage your dependencies, merely add `nameof/x.y.z` to your conan's requires, where `x.y.z` is the release version you want to use. 128 | 129 | Archlinux users can install `nameof` by package manager AUR, using the following command: `yay -S nameof`. 130 | 131 | Alternatively, you can use something like [CPM](https://github.com/TheLartians/CPM) which is based on CMake's `Fetch_Content` module. 132 | 133 | ```cmake 134 | CPMAddPackage( 135 | NAME nameof 136 | GITHUB_REPOSITORY Neargye/nameof 137 | GIT_TAG x.y.z # Where `x.y.z` is the release version you want to use. 138 | ) 139 | ``` 140 | 141 | ## Compiler compatibility 142 | 143 | Check in the [reference](doc/reference.md) for each features. 144 | 145 | ## Licensed under the [MIT License](LICENSE) 146 | -------------------------------------------------------------------------------- /cmake/GenPkgConfig/GenPkgConfig.cmake: -------------------------------------------------------------------------------- 1 | #[=======================================================================[.rst: 2 | 3 | GenPkgConfig 4 | ------------ 5 | 6 | This is the library helping you to generate and install pkg-config files. 7 | 8 | Unlicense 9 | ^^^^^^^^^ 10 | 11 | This is free and unencumbered software released into the public domain. 12 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. 13 | In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 14 | 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 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. 15 | For more information, please refer to 16 | 17 | Warning 18 | ^^^^^^^ 19 | 20 | CMake is currently merging a built-in impl of pkg-config file generator! https://gitlab.kitware.com/cmake/cmake/-/merge_requests/6363 21 | 22 | Functions 23 | ^^^^^^^^^ 24 | .. command:: configure_pkg_config_file 25 | 26 | .. versionadded:: 3.22 27 | 28 | Generates a pkg-config file for 29 | 30 | :: 31 | 32 | configure_pkg_config_file( 33 | NAME 34 | VERSION 35 | DESCRIPTION 36 | URL 37 | COMPONENT 38 | INSTALL_LIB_DIR 39 | INSTALL_INCLUDE_DIR 40 | REQUIRES ... ... 41 | REQUIRES ... ... 42 | ) 43 | 44 | The arguments are optional and usually are not needed to be set if global (not component-specific) CPACK vars have been set before. 45 | 46 | Generation is done in build time using packaging expressions. 47 | 48 | #]=======================================================================] 49 | 50 | set("GEN_PKG_CONFIG_WORKAROUNDS_BUILD_TIME_SCRIPTS" "${CMAKE_CURRENT_LIST_DIR}/buildTimeScripts") 51 | 52 | cmake_policy(SET CMP0070 NEW) 53 | 54 | function(configure_pkg_config_file TARGET) 55 | cmake_parse_arguments("" 56 | "" # options 57 | "NAME;VERSION;DESCRIPTION;URL;COMPONENT;INSTALL_LIB_DIR;INSTALL_INCLUDE_DIR" # one_value_keywords 58 | "REQUIRES;CONFLICTS" # multi_value_keywords 59 | ${ARGN} 60 | ) 61 | 62 | configure_pkg_config_file_vars("${TARGET}" "${_NAME}" "${_INSTALL_LIB_DIR}" "${_INSTALL_INCLUDE_DIR}" "${_COMPONENT}" "${_DESCRIPTION}" "${_URL}" "${_VERSION}" "${_REQUIRES}" "${_CONFLICTS}") 63 | endfunction() 64 | 65 | function(ge_expr_basename inputExpr outVar) 66 | set("${outVar}" "$" PARENT_SCOPE) 67 | endfunction() 68 | 69 | function(configure_pkg_config_file_vars TARGET _NAME _INSTALL_LIB_DIR _INSTALL_INCLUDE_DIR _COMPONENT _DESCRIPTION _URL _VERSION _REQUIRES _CONFLICTS) 70 | #$ 71 | #INTERFACE_LINK_DIRECTORIES 72 | #INTERFACE_LINK_LIBRARIES 73 | #INTERFACE_LINK_OPTIONS 74 | 75 | if(_NAME) 76 | else() 77 | set(_NAME "$") 78 | endif() 79 | 80 | if(_DESCRIPTION) 81 | else() 82 | set(_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION}") 83 | endif() 84 | 85 | if(_VERSION) 86 | else() 87 | set(_VERSION "${CPACK_PACKAGE_VERSION}") 88 | endif() 89 | 90 | if(_URL) 91 | else() 92 | set(_URL "${CPACK_PACKAGE_HOMEPAGE_URL}") 93 | endif() 94 | 95 | if(INSTALL_INCLUDE_DIR) 96 | else() 97 | set(INSTALL_INCLUDE_DIR "${CMAKE_INSTALL_INCLUDEDIR}") 98 | endif() 99 | 100 | if(INSTALL_LIB_DIR) 101 | else() 102 | set(INSTALL_LIB_DIR "${CMAKE_INSTALL_LIBDIR}") 103 | endif() 104 | 105 | set(PKG_CONFIG_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/${_NAME}.pc") 106 | 107 | set(PUBLIC_INCLUDES "$") 108 | set(PUBLIC_LIBS "$") 109 | set(PUBLIC_COMPILE_FLAGS "$") 110 | 111 | set("IS_INTERFACE" "$,INTERFACE_LIBRARY>") 112 | set("IS_OBJECT" "$,OBJECT_LIBRARY>") 113 | get_target_property(CONFIGURE_TIME_TARGET_TYPE "${TARGET}" TYPE) 114 | if(CONFIGURE_TIME_TARGET_TYPE STREQUAL OBJECT_LIBRARY) 115 | set(CONFIGURE_TIME_IS_OBJECT ON) # Special measures have to be taken!!! 116 | endif() 117 | 118 | set("NEEDS_LIBS" "$,$>") 119 | set("NEEDS_LIB_DIR" "$") 120 | string(REPLACE "," "$" NEEDS_LIBS_ESCAPED "${NEEDS_LIBS}") 121 | string(REPLACE ">" "$" NEEDS_LIBS_ESCAPED "${NEEDS_LIBS_ESCAPED}") 122 | 123 | list(APPEND header "prefix=${CMAKE_INSTALL_PREFIX}") 124 | list(APPEND header "$,${NEEDS_LIB_DIR}>,libdir=\${prefix}/${INSTALL_LIB_DIR},>") 125 | list(APPEND header "$,includedir=\${prefix}/${INSTALL_INCLUDE_DIR},>") 126 | 127 | 128 | list(APPEND libSpecific "Name: ${_NAME}") 129 | if(_DESCRIPTION) 130 | list(APPEND libSpecific "Description: ${_DESCRIPTION}") 131 | endif() 132 | if(_URL) 133 | list(APPEND libSpecific "URL: ${_URL}") 134 | endif() 135 | if(_VERSION) 136 | list(APPEND libSpecific "Version: ${_VERSION}") 137 | endif() 138 | if(_REQUIRES) 139 | list(APPEND libSpecific "Requires: ${_REQUIRES}") 140 | endif() 141 | if(_CONFLICTS) 142 | list(APPEND libSpecific "Conflicts: ${_CONFLICTS}") 143 | endif() 144 | 145 | set(OTHER_INCLUDE_FLAGS "-I$, -I>") # Not needed, we can only get build interface flags here. Insert them after -I\${includedir} if you find a way to fix/workaround it 146 | 147 | # Here is a workaround to inability to use TARGET_LINKER_FILE_NAME for targets not involving library generation. 148 | # Strangely $<") # A hack because there is no escape for `$` or `<` or `$<`. So we just disrupt $< into pieces 153 | set(CURRENT_LIB_ESCAPED_BINARY_NAME "${ESCAPED_GENEXPR_BEGINNING}TARGET_LINKER_FILE_NAME:${TARGET}$") 154 | set(LINK_CURRENT_LIB_FLAG "$>") 155 | 156 | if(CONFIGURE_TIME_IS_OBJECT) 157 | set(IS_TARGET_OBJECTS_CONFIGURE_TIME_UNAVAILABLE ON) 158 | if(IS_TARGET_OBJECTS_CONFIGURE_TIME_UNAVAILABLE) 159 | message(WARNING "CMake is shit: There is (at least was at the time of writing of this code) no generator expression to get only basenames of object files. They are also unavailable at configure stage. And there were no CMake generator expressions for making replacements in strings. So we workaround this.") 160 | set(TARGET_OBJECTS_FILE "${TARGET}.obj_list") 161 | set(OBJECTS_FILE_RETRIEVAL_TARGET_NAME "${TARGET}_get_objects_list") 162 | 163 | set(PKGCONFIG_DUMMY_UNFINISHED_GEN_TARGET_NAME "${TARGET}_pkgconfig_unfinished") 164 | set(PKGCONFIG_PATCH_TARGET_NAME "${TARGET}_patch_pkgconfig") 165 | 166 | set(PKG_CONFIG_FILE_NAME_FINISHED "${PKG_CONFIG_FILE_NAME}") 167 | set(PKG_CONFIG_FILE_NAME_UNFINISHED "${PKG_CONFIG_FILE_NAME_FINISHED}.unfinished") 168 | 169 | file(GENERATE OUTPUT "${TARGET_OBJECTS_FILE}" CONTENT "$") 170 | 171 | add_custom_command( 172 | OUTPUT "${TARGET_OBJECTS_FILE}" 173 | COMMENT "A dummy command to workaround cmake limitations" 174 | ) 175 | add_custom_target("${OBJECTS_FILE_RETRIEVAL_TARGET_NAME}" 176 | DEPENDS "${TARGET_OBJECTS_FILE}" 177 | ) 178 | 179 | add_custom_command( 180 | OUTPUT "${PKG_CONFIG_FILE_NAME_FINISHED}" 181 | PRE_BUILD COMMAND ${CMAKE_COMMAND} "-DobjectsFile=\"${TARGET_OBJECTS_FILE}\"" "-DpkgConfigFileUnlinished=\"${PKG_CONFIG_FILE_NAME_UNFINISHED}\"" "-DpkgConfigFileFinal=\"${PKG_CONFIG_FILE_NAME_FINISHED}\"" "-P" "${GEN_PKG_CONFIG_WORKAROUNDS_BUILD_TIME_SCRIPTS}/getObjectFilesBaseNames.cmake" 182 | MAIN_DEPENDENCY "${TARGET_OBJECTS_FILE}" 183 | DEPENDS "${PKG_CONFIG_FILE_NAME_UNFINISHED}" 184 | COMMENT "Working around CMake limitations about getting list of basenames of object files and about lack of generator expressions to modify strings: ${PKG_CONFIG_FILE_NAME_UNFINISHED} + ${TARGET_OBJECTS_FILE} -> ${PKG_CONFIG_FILE_NAME_FINISHED}" 185 | ) 186 | add_custom_target("${PKGCONFIG_PATCH_TARGET_NAME}" ALL 187 | DEPENDS "${PKG_CONFIG_FILE_NAME_FINISHED}" 188 | ) 189 | add_dependencies("${PKGCONFIG_PATCH_TARGET_NAME}" "${OBJECTS_FILE_RETRIEVAL_TARGET_NAME}" "${PKGCONFIG_DUMMY_UNFINISHED_GEN_TARGET_NAME}") 190 | 191 | set(PROPERLY_JOINED_TARGET_OBJECTS "@PROPERLY_JOINED_TARGET_OBJECTS@") 192 | else() 193 | set("RAW_TARGET_OBJECTS" "$") 194 | message(FATAL_ERROR "This branch is unimplemented because CMake lacked the needed functionality at the time") 195 | set(PROPERLY_JOINED_TARGET_OBJECTS "${RAW_TARGET_OBJECTS}") 196 | endif() 197 | endif() 198 | 199 | set(LINK_CURRENT_OBJECT_FLAG "$") 200 | 201 | list(APPEND libSpecific "$,${NEEDS_LIBS},${IS_OBJECT}>,Libs: -L\${libdir} ${LINK_CURRENT_LIB_FLAG} ${LINK_CURRENT_OBJECT_FLAG} $,-l$, -l>,>,>\n$,$>,Cflags: -I\${includedir} $,>,>") 202 | 203 | 204 | list(JOIN header "\n" header) 205 | list(JOIN libSpecific "\n" libSpecific) 206 | set(libSpecific "${header}\n\n${libSpecific}") 207 | 208 | if(CONFIGURE_TIME_IS_OBJECT) 209 | file(GENERATE OUTPUT "${PKG_CONFIG_FILE_NAME_UNFINISHED}" 210 | CONTENT "${libSpecific}" 211 | ) 212 | 213 | # Dummy target for generated files 214 | add_custom_command( 215 | OUTPUT "${PKG_CONFIG_FILE_NAME_UNFINISHED}" 216 | COMMENT "A dummy command to workaround cmake limitations" 217 | ) 218 | add_custom_target("${PKGCONFIG_DUMMY_UNFINISHED_GEN_TARGET_NAME}" 219 | DEPENDS "${PKG_CONFIG_FILE_NAME_UNFINISHED}" 220 | ) 221 | 222 | 223 | install(FILES "${PKG_CONFIG_FILE_NAME_FINISHED}" 224 | DESTINATION "${_INSTALL_LIB_DIR}/pkgconfig" 225 | COMPONENT "${_COMPONENT}" 226 | ) 227 | else() 228 | file(GENERATE OUTPUT "${PKG_CONFIG_FILE_NAME}" 229 | CONTENT "${libSpecific}" 230 | ) 231 | 232 | 233 | install(FILES "${PKG_CONFIG_FILE_NAME}" 234 | DESTINATION "${_INSTALL_LIB_DIR}/pkgconfig" 235 | COMPONENT "${_COMPONENT}" 236 | ) 237 | endif() 238 | endfunction() 239 | 240 | -------------------------------------------------------------------------------- /cmake/GenPkgConfig/ReadMe.md: -------------------------------------------------------------------------------- 1 | GenPkgConfig.cmake 2 | =================== 3 | 4 | A script generating pkg-config files. 5 | 6 | WARNING: CMake [is currently merging own built-in pkgconfig generation implementation](https://gitlab.kitware.com/cmake/cmake/-/merge_requests/6363)! 7 | 8 | If you require such a new version of CMake, you probably should use the built-in impl instead. 9 | 10 | Syntax 11 | ------ 12 | 13 | ```cmake 14 | configure_pkg_config_file( 15 | NAME 16 | VERSION 17 | DESCRIPTION 18 | URL 19 | COMPONENT 20 | INSTALL_LIB_DIR 21 | INSTALL_INCLUDE_DIR 22 | REQUIRES ... ... 23 | REQUIRES ... ... 24 | ) 25 | ``` 26 | 27 | Issuees 28 | ------- 29 | 30 | 1. For `OBJECT` targets we have run into big issues. CMake 31 | 1. Doesn't allow to get the list of object files at configure time 32 | 2. Allows to get a list of object files as a generator exression ... 33 | 3. BUT ... the path to them is full, but we need only file name! 34 | 4. CMake doesn't allow to strip directory path via generator expression 35 | 5. ... neither it allows string editing within generator expressions ... 36 | 37 | so ... we have to create a custom target using a custom CMake script executed separately, but ... 38 | 39 | 6. `file(GENERATE` doesn't properly register dependencies 40 | ... so we have to use `add_custom_command` to say CMake that these files are generated 41 | 42 | 7. And CMake `install(FILES` doesn't mean that the targets generating these files are automatically executed, 43 | 44 | So we have to use `ALL` in `add_custom_target`. 45 | 46 | -------------------------------------------------------------------------------- /cmake/GenPkgConfig/UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /cmake/GenPkgConfig/buildTimeScripts/getObjectFilesBaseNames.cmake: -------------------------------------------------------------------------------- 1 | 2 | message(STATUS "objectsFile ${objectsFile}") 3 | message(STATUS "pkgConfigFileFinal ${pkgConfigFileFinal}") 4 | message(STATUS "pkgConfigFileUnlinished ${pkgConfigFileUnlinished}") 5 | 6 | file(READ "${objectsFile}" TARGET_OBJECTS) 7 | 8 | set(PROPERLY_JOINED_TARGET_OBJECTS "") 9 | 10 | foreach(objFullPath ${TARGET_OBJECTS}) 11 | get_filename_component(objFullPath "${objFullPath}" NAME) 12 | list(APPEND PROPERLY_JOINED_TARGET_OBJECTS "${objFullPath}") 13 | endforeach() 14 | list(JOIN PROPERLY_JOINED_TARGET_OBJECTS " " PROPERLY_JOINED_TARGET_OBJECTS) 15 | 16 | message(STATUS "PROPERLY_JOINED_TARGET_OBJECTS AFTER ${PROPERLY_JOINED_TARGET_OBJECTS}") 17 | 18 | configure_file("${pkgConfigFileUnlinished}" "${pkgConfigFileFinal}" @ONLY) 19 | -------------------------------------------------------------------------------- /doc/limitations.md: -------------------------------------------------------------------------------- 1 | # Limitations 2 | 3 | ## Nameof 4 | 5 | * If the argument does not have a name, the compilation error `"Expression does not have a name."` occurs. 6 | 7 | ## Nameof Type 8 | 9 | * This library uses a compiler-specific hack (based on `__PRETTY_FUNCTION__` / `__FUNCSIG__`). 10 | 11 | * nameof_type and nameof_type_rtti return a compiler-specific type name. 12 | 13 | * To check if nameof_type is supported by your compiler use the macro `NAMEOF_TYPE_SUPPORTED` or constexpr constant `nameof::is_nameof_type_supported`.
14 | If nameof_type is used on an unsupported compiler, a compilation error occurs. To suppress the error define the macro `NAMEOF_TYPE_NO_CHECK_SUPPORT`. 15 | 16 | * To check if nameof_type_rtti is supported by your compiler use macro `NAMEOF_TYPE_RTTI_SUPPORTED` or constexpr constant `nameof::is_nameof_type_rtti_supported`.
17 | If nameof_type_rtti is used on an unsupported compiler, a compilation error occurs. To suppress the error define the macro `NAMEOF_TYPE_NO_CHECK_SUPPORT`. 18 | 19 | * To check is nameof_member supported compiler use macro `NAMEOF_MEMBER_SUPPORTED` or constexpr constant `nameof::is_nameof_member_supported`.
20 | If nameof_member used on unsupported compiler, occurs the compilation error. To suppress error define macro `NAMEOF_TYPE_NO_CHECK_SUPPORT`. 21 | 22 | * To check is nameof_pointer supported compiler use macro `NAMEOF_POINTER_SUPPORTED` or constexpr constant `nameof::is_nameof_pointer_supported`.
23 | If nameof_pointer used on unsupported compiler, occurs the compilation error. To suppress error define macro `NAMEOF_TYPE_NO_CHECK_SUPPORT`. 24 | 25 | ## Nameof Enum 26 | 27 | * This library uses a compiler-specific hack (based on `__PRETTY_FUNCTION__` / `__FUNCSIG__`), which works on Clang >= 5, MSVC >= 15.3 and GCC >= 9. 28 | 29 | * 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. 30 | 31 | * To check if nameof_enum is supported by your compiler use the macro `NAMEOF_ENUM_SUPPORTED` or constexpr constant `nameof::is_nameof_enum_supported`.
32 | If nameof_enum is used on an unsupported compiler, a compilation error occurs. To suppress the error define the macro `NAMEOF_ENUM_NO_CHECK_SUPPORT`. 33 | 34 | * Enum value must be in range `[NAMEOF_ENUM_RANGE_MIN, NAMEOF_ENUM_RANGE_MAX]`. 35 | 36 | * By default `NAMEOF_ENUM_RANGE_MIN = -128`, `NAMEOF_ENUM_RANGE_MAX = 128`. 37 | 38 | * `NAMEOF_ENUM_RANGE_MIN` must be less than or equal to `0` and must be greater than `INT16_MIN`. 39 | 40 | * `NAMEOF_ENUM_RANGE_MAX` must be greater than `0` and must be less than `INT16_MAX`. 41 | 42 | * If another range is needed for all enum types by default, redefine the macro `NAMEOF_ENUM_RANGE_MIN` and `NAMEOF_ENUM_RANGE_MAX`. 43 | 44 | ```cpp 45 | #define NAMEOF_ENUM_RANGE_MIN 0 46 | #define NAMEOF_ENUM_RANGE_MAX 256 47 | #include 48 | ``` 49 | 50 | * If another range is needed for a specific enum type, add specialization `enum_range` for necessary enum type. Specialization of `enum_range` must be injected in `namespace nameof::customize`. 51 | 52 | ```cpp 53 | #include 54 | 55 | enum class number { one = 100, two = 200, three = 300 }; 56 | 57 | template <> 58 | struct nameof::customize::enum_range { 59 | static constexpr int min = 100; 60 | static constexpr int max = 300; 61 | }; 62 | ``` 63 | 64 | * Won't work if a value is aliased, support for enum-aliases is compiler-implementation-defined. 65 | 66 | * Won't work if the enum is a forward declaration. 67 | 68 | * Intellisense Visual Studio may have some problems analyzing `nameof`. 69 | -------------------------------------------------------------------------------- /doc/reference.md: -------------------------------------------------------------------------------- 1 | # Reference 2 | 3 | * [`NAMEOF` obtains simple name of variable, function, macro.](#nameof) 4 | * [`NAMEOF_FULL` obtains full name of variable, function, macro.](#nameof_full) 5 | * [`NAMEOF_RAW` obtains raw name of variable, function, macro.](#nameof_raw) 6 | * [`NAMEOF_ENUM` obtains name of enum variable.](#nameof_enum) 7 | * [`NAMEOF_ENUM_OR` Obtains name of enum variable or default value if enum variable out of range.](#nameof_enum_or) 8 | * [`NAMEOF_ENUM_CONST` obtains name of static storage enum variable.](#nameof_enum_const) 9 | * [`NAMEOF_ENUM_FLAG` obtains name of enum-flags variable.](#nameof_enum_flag) 10 | * [`NAMEOF_TYPE` obtains type name.](#nameof_type) 11 | * [`NAMEOF_FULL_TYPE` obtains full type name.](#nameof_full_type) 12 | * [`NAMEOF_SHORT_TYPE` obtains short type name.](#nameof_short_type) 13 | * [`NAMEOF_TYPE_EXPR` obtains type name of expression.](#nameof_type_expr) 14 | * [`NAMEOF_FULL_TYPE_EXPR` obtains full type name of expression.](#nameof_full_type_expr) 15 | * [`NAMEOF_SHORT_TYPE_EXPR` obtains short type name of expression.](#nameof_short_type_expr) 16 | * [`NAMEOF_TYPE_RTTI` obtains type name, using RTTI.](#nameof_type_rtti) 17 | * [`NAMEOF_FULL_TYPE_RTTI` obtains short type name, using RTTI.](#nameof_full_type_rtti) 18 | * [`NAMEOF_SHORT_TYPE_RTTI` obtains short type name, using RTTI.](#nameof_short_type_rtti) 19 | * [`NAMEOF_MEMBER` obtains name of member.](#nameof_member) 20 | * [`NAMEOF_POINTER` obtains name of a function, a global or class static variable.](#nameof_pointer) 21 | 22 | ## Synopsis 23 | 24 | * Before use, read the [limitations](limitations.md) of functionality. 25 | 26 | * To check is nameof_enum supported compiler use macro `NAMEOF_ENUM_SUPPORTED` or constexpr constant `nameof::is_nameof_enum_supported`.
27 | If nameof_enum used on unsupported compiler, occurs the compilation error. To suppress error define macro `NAMEOF_ENUM_NO_CHECK_SUPPORT`. 28 | 29 | * To check is nameof_type supported compiler use macro `NAMEOF_TYPE_SUPPORTED` or constexpr constant `nameof::is_nameof_type_supported`.
30 | If nameof_type used on unsupported compiler, occurs the compilation error. To suppress error define macro `NAMEOF_TYPE_NO_CHECK_SUPPORT`. 31 | 32 | * To check is nameof_type_rtti supported compiler use macro `NAMEOF_TYPE_RTTI_SUPPORTED` or constexpr constant `nameof::is_nameof_type_rtti_supported`.
33 | If nameof_type used on unsupported compiler, occurs the compilation error. To suppress error define macro `NAMEOF_TYPE_NO_CHECK_SUPPORT`. 34 | 35 | * To check is nameof_member supported compiler use macro `NAMEOF_MEMBER_SUPPORTED` or constexpr constant `nameof::is_nameof_member_supported`.
36 | If nameof_member used on unsupported compiler, occurs the compilation error. To suppress error define macro `NAMEOF_TYPE_NO_CHECK_SUPPORT`. 37 | 38 | * To check is nameof_pointer supported compiler use macro `NAMEOF_POINTER_SUPPORTED` or constexpr constant `nameof::is_nameof_pointer_supported`.
39 | If nameof_pointer used on unsupported compiler, occurs the compilation error. To suppress error define macro `NAMEOF_TYPE_NO_CHECK_SUPPORT`. 40 | 41 | * To add custom enum or type names see the [example](../example/example_custom_name.cpp). 42 | 43 | * To change the type of strings, use special macros: 44 | 45 | ```cpp 46 | #include 47 | #include 48 | #define NAMEOF_USING_ALIAS_STRING using string = my_lib::String; 49 | #define NAMEOF_USING_ALIAS_STRING_VIEW using string_view = my_lib::StringView; 50 | #include 51 | ``` 52 | 53 | ## `NAMEOF` 54 | 55 | * Obtains name of variable, function, macro. 56 | 57 | * Returns `nameof::cstring` - constexpr implementation of an string. Marked `constexpr` and `noexcept`. 58 | 59 | * If argument does not have name, occurs the compilation error `"Expression does not have a name."`. 60 | 61 | * Examples 62 | 63 | ```cpp 64 | // Name of variable. 65 | NAMEOF(somevar) -> "somevar" 66 | 67 | // Name of member variable. 68 | NAMEOF(person.address.zip_code) -> "zip_code" 69 | 70 | // Name of function. 71 | NAMEOF(foo()) -> "foo" 72 | 73 | // Name of member function. 74 | NAMEOF(somevar.some_method()) -> "some_method" 75 | NAMEOF(somevar.some_method()) -> "some_method" 76 | 77 | // Name of macro. 78 | NAMEOF(__LINE__) -> "__LINE__" 79 | NAMEOF(NAMEOF(structvar)) -> "NAMEOF" 80 | ``` 81 | 82 | * Compiler compatibility 83 | Clang/LLVM >= 5 and C++ >= 17
84 | Visual Studio >= 2017 and C++ >= 17
85 | GCC >= 7 and C++ >= 17
86 | 87 | ## `NAMEOF_FULL` 88 | 89 | * Obtains full (with template suffix) name of variable, function, macro. 90 | 91 | * Returns `nameof::cstring` - constexpr implementation of an string. Marked `constexpr` and `noexcept`. 92 | 93 | * If argument does not have name, occurs the compilation error `"Expression does not have a name."`. 94 | 95 | * Examples 96 | 97 | ```cpp 98 | // Full name of template function. 99 | NAMEOF_FULL(foo()) -> "foo" 100 | 101 | // Full name of template member function. 102 | NAMEOF_FULL(somevar.some_method()) -> "some_method" 103 | ``` 104 | 105 | * Compiler compatibility 106 | Clang/LLVM >= 5 and C++ >= 17
107 | Visual Studio >= 2017 and C++ >= 17
108 | GCC >= 7 and C++ >= 17
109 | 110 | ## `NAMEOF_RAW` 111 | 112 | * Obtains raw name of variable, function, macro. 113 | 114 | * Returns `nameof::cstring` - constexpr implementation of an string. Marked `constexpr` and `noexcept`. 115 | 116 | * If argument does not have name, occurs the compilation error `"Expression does not have a name."`. 117 | 118 | * Examples 119 | 120 | ```cpp 121 | NAMEOF_RAW(::somevar.somefield) -> "::somevar.somefield" 122 | NAMEOF_RAW(&some_class::some_method) -> "&some_class::some_method" 123 | ``` 124 | 125 | * Compiler compatibility 126 | Clang/LLVM >= 5 and C++ >= 17
127 | Visual Studio >= 2017 and C++ >= 17
128 | GCC >= 7 and C++ >= 17
129 | 130 | ## `NAMEOF_ENUM` 131 | 132 | * Obtains name of enum variable. 133 | 134 | * Returns `string_view`. Marked `constexpr` and `noexcept`. 135 | 136 | * If argument does not have name or [out of range](limitations.md#nameof-enum), returns empty `string_view`, in debug occurs assert. 137 | 138 | * Examples 139 | 140 | ```cpp 141 | auto color = Color::RED; 142 | NAMEOF_ENUM(color) -> "RED" 143 | nameof::nameof_enum(color) -> "RED" 144 | ``` 145 | 146 | * Compiler compatibility 147 | Clang/LLVM >= 5 and C++ >= 17
148 | Visual Studio >= 2017 and C++ >= 17
149 | GCC >= 9 and C++ >= 17
150 | 151 | # `NAMEOF_ENUM_OR` 152 | 153 | * Obtains name of enum variable or default value if enum variable out of range. 154 | 155 | * Returns `string`. 156 | 157 | * If argument does not have name or [out of range](limitations.md#nameof-enum), returns `default_value`. 158 | 159 | ```cpp 160 | auto color = Color::RED; 161 | NAMEOF_ENUM_OR(color, "none") -> "RED" 162 | NAMEOF_ENUM_OR((Color)-1, "none") -> "none" 163 | nameof::nameof_enum_or(color, "none") -> "RED" 164 | nameof::nameof_enum_or((Color)-1, "none") -> "none" 165 | ``` 166 | 167 | * Compiler compatibility 168 | Clang/LLVM >= 5 and C++ >= 17
169 | Visual Studio >= 2017 and C++ >= 17
170 | GCC >= 9 and C++ >= 17
171 | 172 | ## `NAMEOF_ENUM_CONST` 173 | 174 | * Obtains name of static storage enum variable. 175 | 176 | * Returns `string_view`. Marked `constexpr` and `noexcept`. 177 | 178 | * This version is much lighter on the compile times and is not restricted to the enum_range [limitation](limitations.md#nameof-enum). 179 | 180 | * If argument does not have name, occurs the compilation error `"Enum value does not have a name."`. 181 | 182 | * Examples 183 | 184 | ```cpp 185 | NAMEOF_ENUM_CONST(Color::GREEN) -> "GREEN" 186 | nameof::nameof_enum() -> "GREEN" 187 | ``` 188 | 189 | * Compiler compatibility 190 | Clang/LLVM >= 5 and C++ >= 17
191 | Visual Studio >= 2017 and C++ >= 17
192 | GCC >= 9 and C++ >= 17
193 | 194 | ## `NAMEOF_ENUM_FLAG` 195 | 196 | * Obtains name of enum flag variable. 197 | 198 | * Returns `string`. 199 | 200 | * If argument does not have name or [out of range](limitations.md#nameof-enum), returns empty `string`, in debug occurs assert. 201 | 202 | * Examples 203 | 204 | ```cpp 205 | enum class AnimalFlags { HasClaws = 1 << 0, CanFly = 1 << 1, EatsFish = 1 << 2, Endangered = 1 << 3 }; 206 | auto flag = AnimalFlags::Endangered; 207 | 208 | NAMEOF_ENUM_FLAG(flag) -> "Endangered" 209 | nameof_enum_flag(flag) -> "Endangered" 210 | 211 | flag = AnimalFlags::CanFly | AnimalFlags::Endangered; 212 | NAMEOF_ENUM_FLAG(flag) -> "CanFly|Endangered" 213 | nameof_enum_flag(flag) -> "CanFly|Endangered" 214 | nameof_enum_flag(flag, '$') -> "CanFly$Endangered" 215 | 216 | NAMEOF_ENUM(HasClaws | CanFly) -> "" 217 | nameof_enum(HasClaws | CanFly) -> "" 218 | ``` 219 | 220 | * Compiler compatibility 221 | Clang/LLVM >= 5 and C++ >= 17
222 | Visual Studio >= 2017 and C++ >= 17
223 | GCC >= 9 and C++ >= 17
224 | 225 | ## `NAMEOF_TYPE` 226 | 227 | * Obtains type name, reference and cv-qualifiers are ignored. 228 | 229 | * Returns `string_view`. Marked `constexpr` and `noexcept`. 230 | 231 | * In all cases, reference and cv-qualifiers are ignored by `NAMEOF_TYPE` (that is, `NAMEOF_TYPE(const T&) == NAMEOF_TYPE(T)`). 232 | 233 | * Returns compiler-specific type name. 234 | 235 | * If type does not have name, occurs the compilation error `"Type does not have a name."`. 236 | 237 | 238 | * Examples 239 | 240 | ```cpp 241 | using T = const int&; 242 | NAMEOF_TYPE(T) -> "int" 243 | nameof::nameof_type() -> "int" 244 | ``` 245 | 246 | * Compiler compatibility 247 | Clang/LLVM >= 5 and C++ >= 17
248 | Visual Studio >= 2017 and C++ >= 17
249 | GCC >= 7 and C++ >= 17
250 | 251 | ## `NAMEOF_FULL_TYPE` 252 | 253 | * Obtains full type name, with reference and cv-qualifiers. 254 | 255 | * Returns `string_view`. Marked `constexpr` and `noexcept`. 256 | 257 | * Returns compiler-specific type name. 258 | 259 | * If type does not have name, occurs the compilation error `"Type does not have a name."`. 260 | 261 | * Examples 262 | 263 | ```cpp 264 | using T = const int&; 265 | NAMEOF_TYPE(T) -> "const int&" 266 | nameof::nameof_full_type() -> "const int&" 267 | ``` 268 | 269 | * Compiler compatibility 270 | Clang/LLVM >= 5 and C++ >= 17
271 | Visual Studio >= 2017 and C++ >= 17
272 | GCC >= 7 and C++ >= 17
273 | 274 | ## `NAMEOF_SHORT_TYPE` 275 | 276 | * Obtains short type name. 277 | 278 | * Returns `string_view`. Marked `constexpr` and `noexcept`. 279 | 280 | * Returns compiler-specific type name. 281 | 282 | * If type does not have name, occurs the compilation error `"Type does not have a name."`. 283 | 284 | * Examples 285 | 286 | ```cpp 287 | using T = const my::detail::SomeClass&; 288 | NAMEOF_SHORT_TYPE(T) -> "SomeClass" 289 | nameof::nameof_short_type() -> "SomeClass" 290 | ``` 291 | 292 | * Compiler compatibility 293 | Clang/LLVM >= 5 and C++ >= 17
294 | Visual Studio >= 2017 and C++ >= 17
295 | GCC >= 7 and C++ >= 17
296 | 297 | ## `NAMEOF_TYPE_EXPR` 298 | 299 | * Obtains string name type of expression, reference and cv-qualifiers are ignored. 300 | 301 | * Returns `string_view`. Marked `constexpr` and `noexcept`. 302 | 303 | * Returns compiler-specific type name. 304 | 305 | * In all cases, reference and cv-qualifiers are ignored. 306 | 307 | * If type does not have name, occurs the compilation error `"Type does not have a name."`. 308 | 309 | * Examples 310 | 311 | ```cpp 312 | using T = const int&; 313 | T var = 42; 314 | NAMEOF_TYPE_EXPR(var) -> "int" 315 | nameof::nameof_type() -> "int" 316 | ``` 317 | 318 | * Compiler compatibility 319 | Clang/LLVM >= 5 and C++ >= 17
320 | Visual Studio >= 2017 and C++ >= 17
321 | GCC >= 7 and C++ >= 17
322 | 323 | ## `NAMEOF_FULL_TYPE_EXPR` 324 | 325 | * Obtains full type name of expression, with reference and cv-qualifiers. 326 | 327 | * Returns `string_view`. Marked `constexpr` and `noexcept`. 328 | 329 | * Returns compiler-specific type name. 330 | 331 | * If type does not have name, occurs the compilation error `"Type does not have a name."`. 332 | 333 | * Examples 334 | 335 | ```cpp 336 | using T = const int&; 337 | T var = 42; 338 | NAMEOF_FULL_TYPE_EXPR(var) -> "const int&" 339 | nameof::nameof_full_type() -> "const int&" 340 | ``` 341 | 342 | * Compiler compatibility 343 | Clang/LLVM >= 5 and C++ >= 17
344 | Visual Studio >= 2017 and C++ >= 17
345 | GCC >= 7 and C++ >= 17
346 | 347 | ## `NAMEOF_SHORT_TYPE_EXPR` 348 | 349 | * Obtains short type name of expression. 350 | 351 | * Returns `string_view`. Marked `constexpr` and `noexcept`. 352 | 353 | * Returns compiler-specific type name. 354 | 355 | * If type does not have name, occurs the compilation error `"Type does not have a name."`. 356 | 357 | * Examples 358 | 359 | ```cpp 360 | const my::detail::SomeClass var; 361 | NAMEOF_SHORT_TYPE_EXPR(var) -> "SomeClass" 362 | nameof::nameof_short_type() -> "SomeClass" 363 | ``` 364 | 365 | * Compiler compatibility 366 | Clang/LLVM >= 5 and C++ >= 17
367 | Visual Studio >= 2017 and C++ >= 17
368 | GCC >= 7 and C++ >= 17
369 | 370 | ## `NAMEOF_TYPE_RTTI` 371 | 372 | * Obtains type name, using RTTI. 373 | 374 | * Returns `string`. 375 | 376 | * Examples 377 | 378 | ```cpp 379 | volatile const my::detail::Base* ptr = new my::detail::Derived(); 380 | NAMEOF_TYPE_RTTI(*ptr) -> "my::detail::Derived" 381 | ``` 382 | 383 | * Compiler compatibility 384 | Clang/LLVM >= 5 and C++ >= 17 and RTTI enabled
385 | Visual Studio >= 2017 and C++ >= 17 and RTTI enabled
386 | GCC >= 7 and C++ >= 17 and RTTI enabled
387 | 388 | ## `NAMEOF_FULL_TYPE_RTTI` 389 | 390 | * Obtains full type name, using RTTI. 391 | 392 | * Returns `string`. 393 | 394 | * Examples 395 | 396 | ```cpp 397 | volatile const my::detail::Base* ptr = new my::detail::Derived(); 398 | NAMEOF_FULL_TYPE_RTTI(cv_ref) -> "volatile const my::detail::Derived&" 399 | `` 400 | 401 | ## `NAMEOF_SHORT_TYPE_RTTI` 402 | 403 | * Obtains short type name, using RTTI. 404 | 405 | * Returns `string`. 406 | 407 | * Examples 408 | 409 | ```cpp 410 | volatile const my::detail::Base* ptr = new my::detail::Derived(); 411 | NAMEOF_SHORT_TYPE_RTTI(*ptr) -> "Derived" 412 | ``` 413 | 414 | * Compiler compatibility 415 | Clang/LLVM >= 5 and C++ >= 17 and RTTI enabled
416 | Visual Studio >= 2017 and C++ >= 17 and RTTI enabled
417 | GCC >= 7 and C++ >= 17 and RTTI enabled
418 | 419 | ## `NAMEOF_MEMBER` 420 | 421 | * Obtains name of member. 422 | 423 | * Returns `string_view`. 424 | 425 | * Examples 426 | 427 | ```cpp 428 | struct A { 429 | int this_is_the_name; 430 | }; 431 | // .. 432 | NAMEOF_MEMBER(&A::this_is_the_name) -> "this_is_the_name" 433 | nameof::nameof_member(&A::this_is_the_name) -> "this_is_the_name" 434 | ``` 435 | 436 | * Compiler compatibility 437 | Clang/LLVM >= 5 and C++ >= 17
438 | Visual Studio >= 2022 and C++ >= 20
439 | GCC >= 7 and C++ >= 17
440 | 441 | ## `NAMEOF_POINTER` 442 | 443 | * Obtains name of a function, a global or class static variable. 444 | 445 | * Returns `string_view`. 446 | 447 | * Examples 448 | ```cpp 449 | int someglobalvariable = 0; 450 | // .. 451 | NAMEOF_POINTER(&someglobalconstvariable) == "someglobalconstvariable" 452 | nameof::nameof_pointer(&someglobalconstvariable) == "someglobalconstvariable" 453 | 454 | constexpr auto global_ptr = &someglobalvariable; 455 | NAMEOF_POINTER(global_ptr) == "someglobalconstvariable" 456 | nameof::nameof_pointer(global_ptr) == "someglobalconstvariable" 457 | ``` 458 | 459 | * Compiler compatibility 460 | Clang/LLVM >= 5 and C++ >= 17
461 | Visual Studio >= 2022 and C++ >= 20
462 | GCC >= 7 and C++ >= 17
463 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(CheckCXXCompilerFlag) 2 | 3 | if((CMAKE_CXX_COMPILER_ID MATCHES "GNU") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) 4 | set(OPTIONS -Wall -Wextra -pedantic-errors -Werror) 5 | elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 6 | set(OPTIONS /W4 /WX) 7 | if(HAS_PERMISSIVE_FLAG) 8 | set(OPTIONS ${OPTIONS} /permissive-) 9 | endif() 10 | endif() 11 | 12 | function(make_example target) 13 | add_executable(${target} ${target}.cpp) 14 | set_target_properties(${target} PROPERTIES CXX_EXTENSIONS OFF) 15 | target_compile_features(${target} PRIVATE cxx_std_17) 16 | target_compile_options(${target} PRIVATE ${OPTIONS}) 17 | target_link_libraries(${target} PRIVATE ${CMAKE_PROJECT_NAME}) 18 | endfunction() 19 | 20 | make_example(example) 21 | make_example(example_custom_name) 22 | -------------------------------------------------------------------------------- /example/example.cpp: -------------------------------------------------------------------------------- 1 | // Licensed under the MIT License . 2 | // SPDX-License-Identifier: MIT 3 | // Copyright (c) 2018 - 2024 Daniil Goncharov . 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | struct Base { virtual ~Base() = default; }; 31 | 32 | struct Derived : Base {}; 33 | 34 | struct SomeStruct { 35 | int somefield = 0; 36 | 37 | void SomeMethod1(int i) { somefield = i; } 38 | 39 | int SomeMethod2() const { return somefield; } 40 | }; 41 | 42 | void SomeMethod3() { 43 | std::cout << NAMEOF(SomeMethod3) << " no called!" << std::endl; 44 | } 45 | 46 | template 47 | std::string SomeMethod4(U value) { 48 | auto function_name = NAMEOF(SomeMethod4).str() 49 | .append("<") 50 | .append(NAMEOF_TYPE(T)) 51 | .append(", ") 52 | .append(NAMEOF_TYPE(U)) 53 | .append(">(") 54 | .append(NAMEOF_TYPE(U)) 55 | .append(" ") 56 | .append(NAMEOF(value).data()) 57 | .append(")"); 58 | 59 | return function_name; 60 | } 61 | 62 | template 63 | class SomeClass { 64 | public: 65 | void SomeMethod5() const { 66 | std::cout << nameof::nameof_type() << std::endl; 67 | } 68 | 69 | template 70 | C SomeMethod6() const { 71 | C t{}; 72 | std::cout << NAMEOF_TYPE_EXPR(t) << std::endl; 73 | return t; 74 | } 75 | }; 76 | 77 | struct Long { 78 | struct LL { 79 | int field = 0; 80 | }; 81 | LL ll; 82 | }; 83 | 84 | enum class Color { RED, GREEN, BLUE }; 85 | 86 | enum AnimalFlags { HasClaws = 1 << 0, CanFly = 1 << 1, EatsFish = 1 << 2, Endangered = 1 << 3 }; 87 | 88 | SomeStruct structvar; 89 | Long othervar; 90 | SomeStruct* ptrvar = &structvar; 91 | 92 | void name_to_chars(const char* name) { 93 | std::cout << name << std::endl; 94 | } 95 | 96 | void name_to_string(const std::string& name) { 97 | std::cout << name << std::endl; 98 | } 99 | 100 | void name_to_string_view(std::string_view name) { 101 | std::cout << name << std::endl; 102 | } 103 | 104 | int main() { 105 | // Compile-time. 106 | constexpr auto name = NAMEOF(structvar); 107 | using namespace std::literals::string_view_literals; 108 | static_assert("structvar"sv == name); 109 | 110 | name_to_chars(name.c_str()); // 'structvar' 111 | // or name_to_chars(name.data()); 112 | // Note: c_str() return name as null-terminated C string, no memory allocation. 113 | 114 | name_to_string(name.str()); // 'structvar' 115 | // Note: str() occure memory allocation to copy name to std::string. 116 | // or name_to_string(std::string{name}); 117 | // or name_to_string(static_cast(name)); 118 | // Note: cast to std::string occure memory allocation to copy name to std::string. 119 | 120 | name_to_string_view(name); // 'structvar' 121 | // Note: Implicit cast to std::string_view, no memory allocation. 122 | 123 | #if defined(NAMEOF_ENUM_SUPPORTED) 124 | // Nameof enum variable. 125 | auto color = Color::RED; 126 | std::cout << nameof::nameof_enum(color) << std::endl; // 'RED' 127 | std::cout << NAMEOF_ENUM(color) << std::endl; // 'RED' 128 | std::cout << nameof::nameof_enum() << std::endl; // 'GREEN' 129 | 130 | // Nameof enum flags. 131 | auto flag = static_cast(AnimalFlags::CanFly | AnimalFlags::EatsFish); 132 | std::cout << nameof::nameof_enum_flag(flag) << std::endl; // 'CanFly|EatsFish' 133 | std::cout << NAMEOF_ENUM_FLAG(flag) << std::endl; // 'CanFly|EatsFish' 134 | #endif 135 | 136 | // Nameof. 137 | std::cout << NAMEOF(structvar) << std::endl; // 'structvar' 138 | std::cout << NAMEOF(::structvar) << std::endl; // 'structvar' 139 | std::cout << NAMEOF(structvar.somefield) << std::endl; // 'somefield' 140 | std::cout << NAMEOF((&structvar)->somefield) << std::endl; // 'somefield' 141 | std::cout << NAMEOF(othervar.ll.field) << std::endl; // 'field' 142 | std::cout << NAMEOF(ptrvar) << std::endl; // 'ptrvar 143 | 144 | // Nameof function. 145 | std::cout << NAMEOF(&SomeStruct::SomeMethod1) << std::endl; // 'SomeMethod1' 146 | std::cout << NAMEOF(structvar.SomeMethod2()) << std::endl; // 'SomeMethod2' 147 | std::cout << NAMEOF(SomeMethod3) << std::endl; // 'SomeMethod3' 148 | std::cout << NAMEOF(SomeMethod4(1.0f)) << std::endl; // 'SomeMethod4' 149 | std::cout << NAMEOF(&SomeClass::SomeMethod5) << std::endl; // 'SomeMethod5' 150 | std::cout << NAMEOF(&SomeClass::SomeMethod6) << std::endl; // 'SomeMethod6' 151 | 152 | // Nameof with template suffix. 153 | std::cout << NAMEOF_FULL(SomeMethod4) << std::endl; // 'SomeMethod4' 154 | std::cout << NAMEOF_FULL(&SomeClass::SomeMethod6) << std::endl; // 'SomeMethod6' 155 | 156 | // Nameof type. 157 | std::cout << nameof::nameof_type() << std::endl; // 'Long::LL' 158 | std::cout << NAMEOF_TYPE(const Long::LL&) << std::endl; // 'Long::LL' 159 | std::cout << nameof::nameof_full_type() << std::endl; // 'const Long::LL &' 160 | std::cout << NAMEOF_FULL_TYPE(const Long::LL&) << std::endl; // 'const Long::LL &' 161 | std::cout << NAMEOF_SHORT_TYPE(const Long::LL&) << std::endl; // 'LL' 162 | std::cout << NAMEOF_SHORT_TYPE(const SomeClass&) << std::endl; // 'SomeClass' 163 | 164 | // Nameof variable type. 165 | std::cout << nameof::nameof_type() << std::endl; // 'SomeStruct' 166 | std::cout << NAMEOF_TYPE_EXPR(structvar) << std::endl; // 'SomeStruct' 167 | std::cout << NAMEOF_TYPE_EXPR(std::declval>()) << std::endl; // 'SomeClass' 168 | std::cout << NAMEOF_FULL_TYPE_EXPR(std::declval>()) << std::endl; // 'const SomeClass &&' 169 | std::cout << NAMEOF_SHORT_TYPE_EXPR(std::declval>()) << std::endl; // 'SomeClass' 170 | 171 | #if defined(NAMEOF_MEMBER_SUPPORTED) 172 | // Nameof member 173 | std::cout << nameof::nameof_member<&SomeStruct::somefield>() << std::endl; // somefield 174 | std::cout << nameof::nameof_member<&SomeStruct::SomeMethod1>() << std::endl; // SomeMethod1 175 | std::cout << NAMEOF_MEMBER(&Long::LL::field) << std::endl; // field 176 | constexpr auto member_ptr = &SomeStruct::somefield; 177 | std::cout << NAMEOF_MEMBER(member_ptr) << std::endl; // somefield 178 | #endif 179 | 180 | // Nameof macro. 181 | std::cout << NAMEOF(__LINE__) << std::endl; // '__LINE__' 182 | 183 | // Nameof raw. 184 | std::cout << NAMEOF_RAW(structvar.somefield) << std::endl; // 'structvar.somefield' 185 | std::cout << NAMEOF_RAW(&SomeStruct::SomeMethod1) << std::endl; // '&SomeStruct::SomeMethod1' 186 | 187 | #if defined(NAMEOF_TYPE_RTTI_SUPPORTED) 188 | // Nameof type using RTTI. 189 | Base* ptr = new Derived(); 190 | std::cout << NAMEOF_TYPE_RTTI(ptr) << std::endl; // 'Base *' 191 | std::cout << NAMEOF_TYPE_RTTI(*ptr) << std::endl; // 'Derived' 192 | #endif 193 | 194 | // Some more complex example. 195 | 196 | std::cout << SomeMethod4(structvar) << std::endl; // 'SomeMethod4(SomeStruct value)' 197 | 198 | auto div = [](int x, int y) -> int { 199 | if (y == 0) { 200 | throw std::invalid_argument(NAMEOF(y).str() + " should not be zero!"); 201 | } 202 | return x / y; 203 | }; 204 | 205 | try { 206 | auto z = div(10, 0); 207 | std::cout << z << std::endl; 208 | } catch (const std::exception& e) { 209 | std::cout << e.what() << std::endl; // 'y should not be zero!' 210 | } 211 | 212 | /* Remarks */ 213 | #if 0 214 | // This expression does not have name. 215 | std::cout << NAMEOF(ptrvar[0]) << std::endl; // '' 216 | std::cout << NAMEOF(42.0) << std::endl; // '' 217 | std::cout << NAMEOF(42) << std::endl; // '' 218 | std::cout << NAMEOF(42.0_deg) << std::endl; // '' 219 | std::cout << NAMEOF((structvar)) << std::endl; // '' 220 | std::cout << NAMEOF((SomeMethod4)(1.0f)) << std::endl; // '' 221 | std::cout << NAMEOF(42, 42, 42) << std::endl; // '' 222 | std::cout << NAMEOF(42 + 42) << std::endl; // '' 223 | std::cout << NAMEOF("Bad case"_string) << std::endl; // '' 224 | std::cout << NAMEOF("Bad case") << std::endl; // '' 225 | std::cout << NAMEOF("somevar.somefield") << std::endl; // '' 226 | std::cout << NAMEOF(42.f) << std::endl; // '' 227 | std::cout << NAMEOF(structvar.somefield++) << std::endl; // '' 228 | std::cout << NAMEOF(std::string{"test"}) << std::endl; // '' 229 | #endif 230 | 231 | return 0; 232 | } 233 | -------------------------------------------------------------------------------- /example/example_custom_name.cpp: -------------------------------------------------------------------------------- 1 | // Licensed under the MIT License . 2 | // SPDX-License-Identifier: MIT 3 | // Copyright (c) 2020 - 2024 Daniil Goncharov . 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include 24 | 25 | #include 26 | 27 | enum class Color { RED = -10, BLUE = 0, GREEN = 10 }; 28 | enum class Numbers { One, Two, Three }; 29 | 30 | #if defined(NAMEOF_ENUM_SUPPORTED) 31 | // Сustom definitions of names for enum. 32 | // Specialization of `enum_name` must be injected in `namespace nameof::customize`. 33 | template <> 34 | constexpr std::string_view nameof::customize::enum_name(Color value) noexcept { 35 | switch (value) { 36 | case Color::RED: 37 | return "the red color"; 38 | case Color::BLUE: 39 | return "The BLUE"; 40 | case Color::GREEN: 41 | return {}; // Empty string for default value. 42 | } 43 | return {}; // Empty string for unknown value. 44 | } 45 | 46 | // Сustom definitions of names for enum. 47 | // Specialization of `enum_name` must be injected in `namespace nameof::customize`. 48 | template <> 49 | constexpr std::string_view nameof::customize::enum_name(Numbers value) noexcept { 50 | switch (value) { 51 | case Numbers::One: 52 | return "the one"; 53 | default: 54 | return {}; // Empty string for default or unknown value. 55 | } 56 | } 57 | #endif 58 | 59 | // Сustom definitions of names for type. 60 | // Specialization of `type_name` must be injected in `namespace nameof::customize`. 61 | template <> 62 | constexpr std::string_view nameof::customize::type_name() noexcept { 63 | return "The Color"; 64 | } 65 | 66 | class a1_test {}; 67 | class a2_test {}; 68 | 69 | // Сustom definitions of names for type. 70 | // Specialization of `type_name` must be injected in `namespace nameof::customize`. 71 | template <> 72 | constexpr std::string_view nameof::customize::type_name() noexcept { 73 | return "Animal"; 74 | } 75 | 76 | int main() { 77 | #if defined(NAMEOF_ENUM_SUPPORTED) 78 | std::cout << nameof::nameof_enum(Color::RED) << std::endl; // 'the red color' 79 | std::cout << nameof::nameof_enum(Color::BLUE) << std::endl; // 'The BLUE' 80 | std::cout << nameof::nameof_enum(Color::GREEN) << std::endl; // 'GREEN' 81 | 82 | std::cout << nameof::nameof_enum(Numbers::One) << std::endl; // 'the one' 83 | std::cout << nameof::nameof_enum(Numbers::Two) << std::endl; // 'Two' 84 | std::cout << nameof::nameof_enum(Numbers::Three) << std::endl; // 'Three' 85 | #endif 86 | 87 | std::cout << nameof::nameof_type() << std::endl; // 'The Color' 88 | std::cout << nameof::nameof_type() << std::endl; // 'Numbers' 89 | std::cout << nameof::nameof_type() << std::endl; // 'Animal' 90 | std::cout << nameof::nameof_type() << std::endl; // 'a2_test' 91 | 92 | return 0; 93 | } 94 | -------------------------------------------------------------------------------- /include/nameof.hpp: -------------------------------------------------------------------------------- 1 | // _ _ __ _____ 2 | // | \ | | / _| / ____|_ _ 3 | // | \| | __ _ _ __ ___ ___ ___ | |_ | | _| |_ _| |_ 4 | // | . ` |/ _` | '_ ` _ \ / _ \/ _ \| _| | | |_ _|_ _| 5 | // | |\ | (_| | | | | | | __/ (_) | | | |____|_| |_| 6 | // |_| \_|\__,_|_| |_| |_|\___|\___/|_| \_____| 7 | // https://github.com/Neargye/nameof 8 | // version 0.10.4 9 | // 10 | // Licensed under the MIT License . 11 | // SPDX-License-Identifier: MIT 12 | // Copyright (c) 2016 - 2024 Daniil Goncharov . 13 | // 14 | // Permission is hereby granted, free of charge, to any person obtaining a copy 15 | // of this software and associated documentation files (the "Software"), to deal 16 | // in the Software without restriction, including without limitation the rights 17 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | // copies of the Software, and to permit persons to whom the Software is 19 | // furnished to do so, subject to the following conditions: 20 | // 21 | // The above copyright notice and this permission notice shall be included in all 22 | // copies or substantial portions of the Software. 23 | // 24 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | // SOFTWARE. 31 | 32 | #ifndef NEARGYE_NAMEOF_HPP 33 | #define NEARGYE_NAMEOF_HPP 34 | 35 | #define NAMEOF_VERSION_MAJOR 0 36 | #define NAMEOF_VERSION_MINOR 10 37 | #define NAMEOF_VERSION_PATCH 4 38 | 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | #if !defined(NAMEOF_USING_ALIAS_STRING) 50 | # include 51 | #endif 52 | #if !defined(NAMEOF_USING_ALIAS_STRING_VIEW) 53 | # include 54 | #endif 55 | 56 | #if __has_include() 57 | # include 58 | # include 59 | #endif 60 | 61 | #if defined(__clang__) 62 | # pragma clang diagnostic push 63 | # pragma clang diagnostic ignored "-Wunknown-warning-option" 64 | # pragma clang diagnostic ignored "-Wenum-constexpr-conversion" 65 | #elif defined(__GNUC__) 66 | # pragma GCC diagnostic push 67 | # pragma GCC diagnostic ignored "-Wstringop-overflow" // Missing terminating nul 'enum_name_v'. 68 | #elif defined(_MSC_VER) 69 | # pragma warning(push) 70 | # pragma warning(disable : 26495) // Variable 'cstring::chars_' is uninitialized. 71 | # pragma warning(disable : 28020) // Arithmetic overflow: Using operator '-' on a 4 byte value and then casting the result to a 8 byte value. 72 | # pragma warning(disable : 26451) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call. 73 | # pragma warning(disable : 4514) // Unreferenced inline function has been removed. 74 | #endif 75 | 76 | // Checks nameof_type compiler compatibility. 77 | #if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 7 || defined(_MSC_VER) && _MSC_VER >= 1910 78 | # undef NAMEOF_TYPE_SUPPORTED 79 | # define NAMEOF_TYPE_SUPPORTED 1 80 | #endif 81 | 82 | // Checks nameof_type_rtti compiler compatibility. 83 | #if defined(__clang__) 84 | # if __has_feature(cxx_rtti) 85 | # undef NAMEOF_TYPE_RTTI_SUPPORTED 86 | # define NAMEOF_TYPE_RTTI_SUPPORTED 1 87 | # endif 88 | #elif defined(__GNUC__) 89 | # if defined(__GXX_RTTI) 90 | # undef NAMEOF_TYPE_RTTI_SUPPORTED 91 | # define NAMEOF_TYPE_RTTI_SUPPORTED 1 92 | # endif 93 | #elif defined(_MSC_VER) 94 | # if defined(_CPPRTTI) 95 | # undef NAMEOF_TYPE_RTTI_SUPPORTED 96 | # define NAMEOF_TYPE_RTTI_SUPPORTED 1 97 | # endif 98 | #endif 99 | 100 | // Checks nameof_member compiler compatibility. 101 | #if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 7 || defined(_MSC_VER) && defined(_MSVC_LANG) && _MSVC_LANG >= 202002L 102 | # undef NAMEOF_MEMBER_SUPPORTED 103 | # define NAMEOF_MEMBER_SUPPORTED 1 104 | #endif 105 | 106 | // Checks nameof_pointer compiler compatibility. 107 | #if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 7 || defined(_MSC_VER) && defined(_MSVC_LANG) && _MSVC_LANG >= 202002L 108 | # undef NAMEOF_POINTER_SUPPORTED 109 | # define NAMEOF_POINTER_SUPPORTED 1 110 | #endif 111 | 112 | // Checks nameof_enum compiler compatibility. 113 | #if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 9 || defined(_MSC_VER) && _MSC_VER >= 1910 114 | # undef NAMEOF_ENUM_SUPPORTED 115 | # define NAMEOF_ENUM_SUPPORTED 1 116 | #endif 117 | 118 | // Checks nameof_enum compiler aliases compatibility. 119 | #if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 9 || defined(_MSC_VER) && _MSC_VER >= 1920 120 | # undef NAMEOF_ENUM_SUPPORTED_ALIASES 121 | # define NAMEOF_ENUM_SUPPORTED_ALIASES 1 122 | #endif 123 | 124 | // Enum value must be greater or equals than NAMEOF_ENUM_RANGE_MIN. By default NAMEOF_ENUM_RANGE_MIN = -128. 125 | // If need another min range for all enum types by default, redefine the macro NAMEOF_ENUM_RANGE_MIN. 126 | #if !defined(NAMEOF_ENUM_RANGE_MIN) 127 | # define NAMEOF_ENUM_RANGE_MIN -128 128 | #endif 129 | 130 | // Enum value must be less or equals than NAMEOF_ENUM_RANGE_MAX. By default NAMEOF_ENUM_RANGE_MAX = 128. 131 | // If need another max range for all enum types by default, redefine the macro NAMEOF_ENUM_RANGE_MAX. 132 | #if !defined(NAMEOF_ENUM_RANGE_MAX) 133 | # define NAMEOF_ENUM_RANGE_MAX 128 134 | #endif 135 | 136 | namespace nameof { 137 | 138 | // If need another string_view type, define the macro NAMEOF_USING_ALIAS_STRING_VIEW. 139 | #if defined(NAMEOF_USING_ALIAS_STRING_VIEW) 140 | NAMEOF_USING_ALIAS_STRING_VIEW 141 | #else 142 | using std::string_view; 143 | #endif 144 | 145 | // If need another string type, define the macro NAMEOF_USING_ALIAS_STRING. 146 | #if defined(NAMEOF_USING_ALIAS_STRING) 147 | NAMEOF_USING_ALIAS_STRING 148 | #else 149 | using std::string; 150 | #endif 151 | 152 | namespace customize { 153 | 154 | // 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. 155 | // If you need another range for all enum types by default, redefine the macro NAMEOF_ENUM_RANGE_MIN and NAMEOF_ENUM_RANGE_MAX. 156 | // If you need another range for specific enum type, add specialization enum_range for necessary enum type. 157 | template 158 | struct enum_range { 159 | static_assert(std::is_enum_v, "nameof::customize::enum_range requires enum type."); 160 | inline static constexpr int min = NAMEOF_ENUM_RANGE_MIN; 161 | inline static constexpr int max = NAMEOF_ENUM_RANGE_MAX; 162 | static_assert(max > min, "nameof::customize::enum_range requires max > min."); 163 | }; 164 | 165 | static_assert(NAMEOF_ENUM_RANGE_MIN <= 0, "NAMEOF_ENUM_RANGE_MIN must be less or equals than 0."); 166 | static_assert(NAMEOF_ENUM_RANGE_MIN > (std::numeric_limits::min)(), "NAMEOF_ENUM_RANGE_MIN must be greater than INT16_MIN."); 167 | 168 | static_assert(NAMEOF_ENUM_RANGE_MAX > 0, "NAMEOF_ENUM_RANGE_MAX must be greater than 0."); 169 | static_assert(NAMEOF_ENUM_RANGE_MAX < (std::numeric_limits::max)(), "NAMEOF_ENUM_RANGE_MAX must be less than INT16_MAX."); 170 | 171 | static_assert(NAMEOF_ENUM_RANGE_MAX > NAMEOF_ENUM_RANGE_MIN, "NAMEOF_ENUM_RANGE_MAX must be greater than NAMEOF_ENUM_RANGE_MIN."); 172 | 173 | // If you need custom names for enum, add specialization enum_name for necessary enum type. 174 | template 175 | constexpr string_view enum_name(E) noexcept { 176 | static_assert(std::is_enum_v, "nameof::customize::enum_name requires enum type."); 177 | return {}; 178 | } 179 | 180 | // If you need custom name for type, add specialization type_name for necessary type. 181 | template 182 | constexpr string_view type_name() noexcept { 183 | return {}; 184 | } 185 | 186 | // If you need custom name for member, add specialization member_name for necessary type. 187 | template 188 | constexpr string_view member_name() noexcept { 189 | return {}; 190 | } 191 | 192 | // If you need custom name for a pointer, add specialization pointer_name for necessary type. 193 | template 194 | constexpr string_view pointer_name() noexcept { 195 | return {}; 196 | } 197 | 198 | } // namespace nameof::customize 199 | 200 | template 201 | class [[nodiscard]] cstring { 202 | public: 203 | using value_type = const char; 204 | using size_type = std::uint16_t; 205 | using difference_type = std::ptrdiff_t; 206 | using pointer = const char*; 207 | using const_pointer = const char*; 208 | using reference = const char&; 209 | using const_reference = const char&; 210 | 211 | using iterator = const char*; 212 | using const_iterator = const char*; 213 | 214 | using reverse_iterator = std::reverse_iterator; 215 | using const_reverse_iterator = std::reverse_iterator; 216 | 217 | constexpr explicit cstring(string_view str) noexcept : cstring{str, std::make_integer_sequence{}} { 218 | assert(str.size() > 0 && str.size() == N); 219 | } 220 | 221 | constexpr cstring() = delete; 222 | 223 | constexpr cstring(const cstring&) = default; 224 | 225 | constexpr cstring(cstring&&) = default; 226 | 227 | ~cstring() = default; 228 | 229 | cstring& operator=(const cstring&) = default; 230 | 231 | cstring& operator=(cstring&&) = default; 232 | 233 | [[nodiscard]] constexpr const_pointer data() const noexcept { return chars_; } 234 | 235 | [[nodiscard]] constexpr size_type size() const noexcept { return N; } 236 | 237 | [[nodiscard]] constexpr const_iterator begin() const noexcept { return data(); } 238 | 239 | [[nodiscard]] constexpr const_iterator end() const noexcept { return data() + size(); } 240 | 241 | [[nodiscard]] constexpr const_iterator cbegin() const noexcept { return begin(); } 242 | 243 | [[nodiscard]] constexpr const_iterator cend() const noexcept { return end(); } 244 | 245 | [[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept { return end(); } 246 | 247 | [[nodiscard]] constexpr const_reverse_iterator rend() const noexcept { return begin(); } 248 | 249 | [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); } 250 | 251 | [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return rend(); } 252 | 253 | [[nodiscard]] constexpr const_reference operator[](size_type i) const noexcept { return assert(i < size()), chars_[i]; } 254 | 255 | [[nodiscard]] constexpr const_reference front() const noexcept { return chars_[0]; } 256 | 257 | [[nodiscard]] constexpr const_reference back() const noexcept { return chars_[N]; } 258 | 259 | [[nodiscard]] constexpr size_type length() const noexcept { return size(); } 260 | 261 | [[nodiscard]] constexpr bool empty() const noexcept { return false; } 262 | 263 | [[nodiscard]] constexpr int compare(string_view str) const noexcept { return string_view{data(), size()}.compare(str); } 264 | 265 | [[nodiscard]] constexpr const char* c_str() const noexcept { return data(); } 266 | 267 | [[nodiscard]] string str() const { return {begin(), end()}; } 268 | 269 | [[nodiscard]] constexpr operator string_view() const noexcept { return {data(), size()}; } 270 | 271 | [[nodiscard]] constexpr explicit operator const_pointer() const noexcept { return data(); } 272 | 273 | [[nodiscard]] explicit operator string() const { return {begin(), end()}; } 274 | 275 | private: 276 | template 277 | constexpr cstring(string_view str, std::integer_sequence) noexcept : chars_{str[I]..., '\0'} {} 278 | 279 | char chars_[static_cast(N) + 1]; 280 | }; 281 | 282 | template <> 283 | class [[nodiscard]] cstring<0> { 284 | public: 285 | using value_type = const char; 286 | using size_type = std::uint16_t; 287 | using difference_type = std::ptrdiff_t; 288 | using pointer = const char*; 289 | using const_pointer = const char*; 290 | using reference = const char&; 291 | using const_reference = const char&; 292 | 293 | using iterator = const char*; 294 | using const_iterator = const char*; 295 | 296 | using reverse_iterator = std::reverse_iterator; 297 | using const_reverse_iterator = std::reverse_iterator; 298 | 299 | constexpr explicit cstring(string_view) noexcept {} 300 | 301 | constexpr cstring() = default; 302 | 303 | constexpr cstring(const cstring&) = default; 304 | 305 | constexpr cstring(cstring&&) = default; 306 | 307 | ~cstring() = default; 308 | 309 | cstring& operator=(const cstring&) = default; 310 | 311 | cstring& operator=(cstring&&) = default; 312 | 313 | [[nodiscard]] constexpr const_pointer data() const noexcept { return nullptr; } 314 | 315 | [[nodiscard]] constexpr size_type size() const noexcept { return 0; } 316 | 317 | [[nodiscard]] constexpr const_iterator begin() const noexcept { return nullptr; } 318 | 319 | [[nodiscard]] constexpr const_iterator end() const noexcept { return nullptr; } 320 | 321 | [[nodiscard]] constexpr const_iterator cbegin() const noexcept { return nullptr; } 322 | 323 | [[nodiscard]] constexpr const_iterator cend() const noexcept { return nullptr; } 324 | 325 | [[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept { return {}; } 326 | 327 | [[nodiscard]] constexpr const_reverse_iterator rend() const noexcept { return {}; } 328 | 329 | [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { return {}; } 330 | 331 | [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return {}; } 332 | 333 | [[nodiscard]] constexpr size_type length() const noexcept { return 0; } 334 | 335 | [[nodiscard]] constexpr bool empty() const noexcept { return true; } 336 | 337 | [[nodiscard]] constexpr int compare(string_view str) const noexcept { return string_view{}.compare(str); } 338 | 339 | [[nodiscard]] constexpr const char* c_str() const noexcept { return nullptr; } 340 | 341 | [[nodiscard]] string str() const { return {}; } 342 | 343 | [[nodiscard]] constexpr operator string_view() const noexcept { return {}; } 344 | 345 | [[nodiscard]] constexpr explicit operator const_pointer() const noexcept { return nullptr; } 346 | 347 | [[nodiscard]] explicit operator string() const { return {}; } 348 | }; 349 | 350 | template 351 | [[nodiscard]] constexpr bool operator==(const cstring& lhs, string_view rhs) noexcept { 352 | return lhs.compare(rhs) == 0; 353 | } 354 | 355 | template 356 | [[nodiscard]] constexpr bool operator==(string_view lhs, const cstring& rhs) noexcept { 357 | return lhs.compare(rhs) == 0; 358 | } 359 | 360 | template 361 | [[nodiscard]] constexpr bool operator!=(const cstring& lhs, string_view rhs) noexcept { 362 | return lhs.compare(rhs) != 0; 363 | } 364 | 365 | template 366 | [[nodiscard]] constexpr bool operator!=(string_view lhs, const cstring& rhs) noexcept { 367 | return lhs.compare(rhs) != 0; 368 | } 369 | 370 | template 371 | [[nodiscard]] constexpr bool operator>(const cstring& lhs, string_view rhs) noexcept { 372 | return lhs.compare(rhs) > 0; 373 | } 374 | 375 | template 376 | [[nodiscard]] constexpr bool operator>(string_view lhs, const cstring& rhs) noexcept { 377 | return lhs.compare(rhs) > 0; 378 | } 379 | 380 | template 381 | [[nodiscard]] constexpr bool operator>=(const cstring& lhs, string_view rhs) noexcept { 382 | return lhs.compare(rhs) >= 0; 383 | } 384 | 385 | template 386 | [[nodiscard]] constexpr bool operator>=(string_view lhs, const cstring& rhs) noexcept { 387 | return lhs.compare(rhs) >= 0; 388 | } 389 | 390 | template 391 | [[nodiscard]] constexpr bool operator<(const cstring& lhs, string_view rhs) noexcept { 392 | return lhs.compare(rhs) < 0; 393 | } 394 | 395 | template 396 | [[nodiscard]] constexpr bool operator<(string_view lhs, const cstring& rhs) noexcept { 397 | return lhs.compare(rhs) < 0; 398 | } 399 | 400 | template 401 | [[nodiscard]] constexpr bool operator<=(const cstring& lhs, string_view rhs) noexcept { 402 | return lhs.compare(rhs) <= 0; 403 | } 404 | 405 | template 406 | [[nodiscard]] constexpr bool operator<=(string_view lhs, const cstring& rhs) noexcept { 407 | return lhs.compare(rhs) <= 0; 408 | } 409 | 410 | template 411 | std::basic_ostream& operator<<(std::basic_ostream& os, const cstring& srt) { 412 | for (const auto c : srt) { 413 | os.put(c); 414 | } 415 | return os; 416 | } 417 | 418 | namespace detail { 419 | 420 | constexpr string_view pretty_name(string_view name, bool remove_suffix = true) noexcept { 421 | if (name.size() >= 1 && (name[0] == '"' || name[0] == '\'')) { 422 | return {}; // Narrow multibyte string literal. 423 | } else if (name.size() >= 2 && name[0] == 'R' && (name[1] == '"' || name[1] == '\'')) { 424 | return {}; // Raw string literal. 425 | } else if (name.size() >= 2 && name[0] == 'L' && (name[1] == '"' || name[1] == '\'')) { 426 | return {}; // Wide string literal. 427 | } else if (name.size() >= 2 && name[0] == 'U' && (name[1] == '"' || name[1] == '\'')) { 428 | return {}; // UTF-32 encoded string literal. 429 | } else if (name.size() >= 2 && name[0] == 'u' && (name[1] == '"' || name[1] == '\'')) { 430 | return {}; // UTF-16 encoded string literal. 431 | } else if (name.size() >= 3 && name[0] == 'u' && name[1] == '8' && (name[2] == '"' || name[2] == '\'')) { 432 | return {}; // UTF-8 encoded string literal. 433 | } else if (name.size() >= 1 && (name[0] >= '0' && name[0] <= '9')) { 434 | return {}; // Invalid name. 435 | } 436 | 437 | for (std::size_t i = name.size(), h = 0, s = 0; i > 0; --i) { 438 | if (name[i - 1] == ')') { 439 | ++h; 440 | ++s; 441 | continue; 442 | } else if (name[i - 1] == '(') { 443 | --h; 444 | ++s; 445 | continue; 446 | } 447 | 448 | if (h == 0) { 449 | name.remove_suffix(s); 450 | break; 451 | } else { 452 | ++s; 453 | continue; 454 | } 455 | } 456 | 457 | std::size_t s = 0; 458 | for (std::size_t i = name.size(), h = 0; i > 0; --i) { 459 | if (name[i - 1] == '>') { 460 | ++h; 461 | ++s; 462 | continue; 463 | } else if (name[i - 1] == '<') { 464 | --h; 465 | ++s; 466 | continue; 467 | } 468 | 469 | if (h == 0) { 470 | break; 471 | } else { 472 | ++s; 473 | continue; 474 | } 475 | } 476 | 477 | for (std::size_t i = name.size() - s; i > 0; --i) { 478 | if (!((name[i - 1] >= '0' && name[i - 1] <= '9') || 479 | (name[i - 1] >= 'a' && name[i - 1] <= 'z') || 480 | (name[i - 1] >= 'A' && name[i - 1] <= 'Z') || 481 | (name[i - 1] == '_'))) { 482 | name.remove_prefix(i); 483 | break; 484 | } 485 | } 486 | if (remove_suffix) { 487 | name.remove_suffix(s); 488 | } 489 | 490 | if (name.size() > 0 && ((name[0] >= 'a' && name[0] <= 'z') || 491 | (name[0] >= 'A' && name[0] <= 'Z') || 492 | (name[0] == '_'))) { 493 | return name; 494 | } 495 | 496 | return {}; // Invalid name. 497 | } 498 | 499 | #if defined(__cpp_lib_array_constexpr) && __cpp_lib_array_constexpr >= 201603L 500 | # define NAMEOF_ARRAY_CONSTEXPR 1 501 | #else 502 | template 503 | constexpr std::array, N> to_array(T (&a)[N], std::index_sequence) noexcept { 504 | return {{a[I]...}}; 505 | } 506 | #endif 507 | 508 | template 509 | constexpr bool cmp_less(L lhs, R rhs) noexcept { 510 | static_assert(std::is_integral_v && std::is_integral_v, "nameof::detail::cmp_less requires integral type."); 511 | 512 | if constexpr (std::is_signed_v == std::is_signed_v) { 513 | // If same signedness (both signed or both unsigned). 514 | return lhs < rhs; 515 | } else if constexpr (std::is_same_v) { // bool special case 516 | return static_cast(lhs) < rhs; 517 | } else if constexpr (std::is_same_v) { // bool special case 518 | return lhs < static_cast(rhs); 519 | } else if constexpr (std::is_signed_v) { 520 | // If 'right' is negative, then result is 'false', otherwise cast & compare. 521 | return rhs > 0 && lhs < static_cast>(rhs); 522 | } else { 523 | // If 'left' is negative, then result is 'true', otherwise cast & compare. 524 | return lhs < 0 || static_cast>(lhs) < rhs; 525 | } 526 | } 527 | 528 | template 529 | constexpr I log2(I value) noexcept { 530 | static_assert(std::is_integral_v, "nameof::detail::log2 requires integral type."); 531 | 532 | if constexpr (std::is_same_v) { // bool special case 533 | return assert(false), value; 534 | } else { 535 | auto ret = I{0}; 536 | for (; value > I{1}; value >>= I{1}, ++ret) {} 537 | 538 | return ret; 539 | } 540 | } 541 | 542 | template 543 | struct nameof_enum_supported 544 | #if defined(NAMEOF_ENUM_SUPPORTED) && NAMEOF_ENUM_SUPPORTED || defined(NAMEOF_ENUM_NO_CHECK_SUPPORT) 545 | : std::true_type {}; 546 | #else 547 | : std::false_type {}; 548 | #endif 549 | 550 | template 551 | using enable_if_enum_t = std::enable_if_t>, R>; 552 | 553 | template 554 | inline constexpr bool is_enum_v = std::is_enum_v && std::is_same_v>; 555 | 556 | template 557 | constexpr auto n() noexcept { 558 | static_assert(is_enum_v, "nameof::detail::n requires enum type."); 559 | 560 | if constexpr (nameof_enum_supported::value) { 561 | #if defined(__clang__) || defined(__GNUC__) 562 | constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); 563 | #elif defined(_MSC_VER) 564 | constexpr auto name = pretty_name({__FUNCSIG__, sizeof(__FUNCSIG__) - 17}); 565 | #else 566 | constexpr auto name = string_view{}; 567 | #endif 568 | return name; 569 | } else { 570 | return string_view{}; 571 | } 572 | } 573 | 574 | template 575 | constexpr auto enum_name() noexcept { 576 | [[maybe_unused]] constexpr auto custom_name = customize::enum_name(V); 577 | 578 | if constexpr (custom_name.empty()) { 579 | constexpr auto name = n(); 580 | return cstring{name}; 581 | } else { 582 | return cstring{custom_name}; 583 | } 584 | } 585 | 586 | template 587 | inline constexpr auto enum_name_v = enum_name(); 588 | 589 | template 590 | constexpr bool is_valid() noexcept { 591 | #if defined(__clang__) && __clang_major__ >= 16 592 | // https://reviews.llvm.org/D130058, https://reviews.llvm.org/D131307 593 | constexpr E v = __builtin_bit_cast(E, V); 594 | #else 595 | constexpr E v = static_cast(V); 596 | #endif 597 | [[maybe_unused]] constexpr auto custom_name = customize::enum_name(v); 598 | if constexpr (custom_name.empty()) { 599 | return n().size() != 0; 600 | } else { 601 | return custom_name.size() != 0; 602 | } 603 | } 604 | 605 | template > 606 | constexpr U ualue(std::size_t i) noexcept { 607 | if constexpr (std::is_same_v) { // bool special case 608 | static_assert(O == 0, "nameof::detail::ualue requires valid offset."); 609 | 610 | return static_cast(i); 611 | } else if constexpr (IsFlags) { 612 | return static_cast(U{1} << static_cast(static_cast(i) + O)); 613 | } else { 614 | return static_cast(static_cast(i) + O); 615 | } 616 | } 617 | 618 | template > 619 | constexpr E value(std::size_t i) noexcept { 620 | return static_cast(ualue(i)); 621 | } 622 | 623 | template > 624 | constexpr int reflected_min() noexcept { 625 | if constexpr (IsFlags) { 626 | return 0; 627 | } else { 628 | constexpr auto lhs = customize::enum_range::min; 629 | constexpr auto rhs = (std::numeric_limits::min)(); 630 | 631 | if constexpr (cmp_less(rhs, lhs)) { 632 | return lhs; 633 | } else { 634 | return rhs; 635 | } 636 | } 637 | } 638 | 639 | template > 640 | constexpr int reflected_max() noexcept { 641 | if constexpr (IsFlags) { 642 | return std::numeric_limits::digits - 1; 643 | } else { 644 | constexpr auto lhs = customize::enum_range::max; 645 | constexpr auto rhs = (std::numeric_limits::max)(); 646 | 647 | if constexpr (cmp_less(lhs, rhs)) { 648 | return lhs; 649 | } else { 650 | return rhs; 651 | } 652 | } 653 | } 654 | 655 | #define NAMEOF_FOR_EACH_256(T) \ 656 | T( 0)T( 1)T( 2)T( 3)T( 4)T( 5)T( 6)T( 7)T( 8)T( 9)T( 10)T( 11)T( 12)T( 13)T( 14)T( 15)T( 16)T( 17)T( 18)T( 19)T( 20)T( 21)T( 22)T( 23)T( 24)T( 25)T( 26)T( 27)T( 28)T( 29)T( 30)T( 31) \ 657 | T( 32)T( 33)T( 34)T( 35)T( 36)T( 37)T( 38)T( 39)T( 40)T( 41)T( 42)T( 43)T( 44)T( 45)T( 46)T( 47)T( 48)T( 49)T( 50)T( 51)T( 52)T( 53)T( 54)T( 55)T( 56)T( 57)T( 58)T( 59)T( 60)T( 61)T( 62)T( 63) \ 658 | T( 64)T( 65)T( 66)T( 67)T( 68)T( 69)T( 70)T( 71)T( 72)T( 73)T( 74)T( 75)T( 76)T( 77)T( 78)T( 79)T( 80)T( 81)T( 82)T( 83)T( 84)T( 85)T( 86)T( 87)T( 88)T( 89)T( 90)T( 91)T( 92)T( 93)T( 94)T( 95) \ 659 | T( 96)T( 97)T( 98)T( 99)T(100)T(101)T(102)T(103)T(104)T(105)T(106)T(107)T(108)T(109)T(110)T(111)T(112)T(113)T(114)T(115)T(116)T(117)T(118)T(119)T(120)T(121)T(122)T(123)T(124)T(125)T(126)T(127) \ 660 | T(128)T(129)T(130)T(131)T(132)T(133)T(134)T(135)T(136)T(137)T(138)T(139)T(140)T(141)T(142)T(143)T(144)T(145)T(146)T(147)T(148)T(149)T(150)T(151)T(152)T(153)T(154)T(155)T(156)T(157)T(158)T(159) \ 661 | T(160)T(161)T(162)T(163)T(164)T(165)T(166)T(167)T(168)T(169)T(170)T(171)T(172)T(173)T(174)T(175)T(176)T(177)T(178)T(179)T(180)T(181)T(182)T(183)T(184)T(185)T(186)T(187)T(188)T(189)T(190)T(191) \ 662 | T(192)T(193)T(194)T(195)T(196)T(197)T(198)T(199)T(200)T(201)T(202)T(203)T(204)T(205)T(206)T(207)T(208)T(209)T(210)T(211)T(212)T(213)T(214)T(215)T(216)T(217)T(218)T(219)T(220)T(221)T(222)T(223) \ 663 | T(224)T(225)T(226)T(227)T(228)T(229)T(230)T(231)T(232)T(233)T(234)T(235)T(236)T(237)T(238)T(239)T(240)T(241)T(242)T(243)T(244)T(245)T(246)T(247)T(248)T(249)T(250)T(251)T(252)T(253)T(254)T(255) 664 | 665 | template 666 | constexpr void valid_count(bool* valid, std::size_t& count) noexcept { 667 | #define NAMEOF_ENUM_V(O) \ 668 | if constexpr ((I + O) < Size) { \ 669 | if constexpr (is_valid(I + O)>()) { \ 670 | valid[I + O] = true; \ 671 | ++count; \ 672 | } \ 673 | } 674 | 675 | NAMEOF_FOR_EACH_256(NAMEOF_ENUM_V) 676 | 677 | if constexpr ((I + 256) < Size) { 678 | valid_count(valid, count); 679 | } 680 | #undef NAMEOF_ENUM_V 681 | } 682 | 683 | template 684 | struct valid_count_t { 685 | std::size_t count = 0; 686 | bool valid[N] = {}; 687 | }; 688 | 689 | template 690 | constexpr auto valid_count() noexcept { 691 | valid_count_t vc; 692 | valid_count(vc.valid, vc.count); 693 | return vc; 694 | } 695 | 696 | template 697 | constexpr auto values() noexcept { 698 | constexpr auto vc = valid_count(); 699 | 700 | if constexpr (vc.count > 0) { 701 | #if defined(NAMEOF_ARRAY_CONSTEXPR) 702 | std::array values = {}; 703 | #else 704 | E values[vc.count] = {}; 705 | #endif 706 | for (std::size_t i = 0, v = 0; v < vc.count; ++i) { 707 | if (vc.valid[i]) { 708 | values[v++] = value(i); 709 | } 710 | } 711 | #if defined(NAMEOF_ARRAY_CONSTEXPR) 712 | return values; 713 | #else 714 | return to_array(values, std::make_index_sequence{}); 715 | #endif 716 | } else { 717 | return std::array{}; 718 | } 719 | } 720 | 721 | template > 722 | constexpr auto values() noexcept { 723 | constexpr auto min = reflected_min(); 724 | constexpr auto max = reflected_max(); 725 | constexpr auto range_size = max - min + 1; 726 | static_assert(range_size > 0, "nameof::enum_range requires valid size."); 727 | static_assert(range_size < (std::numeric_limits::max)(), "nameof::enum_range requires valid size."); 728 | 729 | return values(); 730 | } 731 | 732 | template 733 | inline constexpr auto values_v = values(); 734 | 735 | template 736 | inline constexpr auto count_v = values_v.size(); 737 | 738 | template > 739 | inline constexpr auto min_v = (count_v > 0) ? static_cast(values_v.front()) : U{0}; 740 | 741 | template > 742 | inline constexpr auto max_v = (count_v > 0) ? static_cast(values_v.back()) : U{0}; 743 | 744 | template 745 | constexpr auto names(std::index_sequence) noexcept { 746 | constexpr auto names = std::array{{enum_name_v[I]>...}}; 747 | return names; 748 | } 749 | 750 | template 751 | inline constexpr auto names_v = names(std::make_index_sequence>{}); 752 | 753 | template > 754 | constexpr bool is_sparse() noexcept { 755 | if constexpr (count_v == 0) { 756 | return false; 757 | } else if constexpr (std::is_same_v) { // bool special case 758 | return false; 759 | } else { 760 | constexpr auto max = IsFlags ? log2(max_v) : max_v; 761 | constexpr auto min = IsFlags ? log2(min_v) : min_v; 762 | constexpr auto range_size = max - min + 1; 763 | 764 | return range_size != count_v; 765 | } 766 | } 767 | 768 | template 769 | inline constexpr bool is_sparse_v = is_sparse(); 770 | 771 | template > 772 | constexpr E enum_value(std::size_t i) noexcept { 773 | if constexpr (is_sparse_v) { 774 | return values_v[i]; 775 | } else { 776 | constexpr auto min = IsFlags ? log2(min_v) : min_v; 777 | 778 | return value(i); 779 | } 780 | } 781 | 782 | template 783 | struct nameof_type_supported 784 | #if defined(NAMEOF_TYPE_SUPPORTED) && NAMEOF_TYPE_SUPPORTED || defined(NAMEOF_TYPE_NO_CHECK_SUPPORT) 785 | : std::true_type {}; 786 | #else 787 | : std::false_type {}; 788 | #endif 789 | 790 | template 791 | struct nameof_type_rtti_supported 792 | #if defined(NAMEOF_TYPE_RTTI_SUPPORTED) && NAMEOF_TYPE_RTTI_SUPPORTED || defined(NAMEOF_TYPE_NO_CHECK_SUPPORT) 793 | : std::true_type {}; 794 | #else 795 | : std::false_type {}; 796 | #endif 797 | 798 | template 799 | struct nameof_member_supported 800 | #if defined(NAMEOF_MEMBER_SUPPORTED) && NAMEOF_MEMBER_SUPPORTED || defined(NAMEOF_TYPE_NO_CHECK_SUPPORT) 801 | : std::true_type {}; 802 | #else 803 | : std::false_type {}; 804 | #endif 805 | 806 | template 807 | struct nameof_pointer_supported 808 | #if defined(NAMEOF_POINTER_SUPPORTED) && NAMEOF_POINTER_SUPPORTED || defined(NAMEOF_TYPE_NO_CHECK_SUPPORT) 809 | : std::true_type {}; 810 | #else 811 | : std::false_type {}; 812 | #endif 813 | 814 | #if defined(_MSC_VER) && !defined(__clang__) 815 | template 816 | struct identity { 817 | using type = T; 818 | }; 819 | #else 820 | template 821 | using identity = T; 822 | #endif 823 | 824 | template 825 | using remove_cvref_t = std::remove_cv_t>; 826 | 827 | template 828 | using enable_if_has_short_name_t = std::enable_if_t && !std::is_pointer_v, R>; 829 | 830 | template 831 | constexpr auto n() noexcept { 832 | #if defined(_MSC_VER) && !defined(__clang__) 833 | [[maybe_unused]] constexpr auto custom_name = customize::type_name(); 834 | #else 835 | [[maybe_unused]] constexpr auto custom_name = customize::type_name(); 836 | #endif 837 | 838 | if constexpr (custom_name.empty() && nameof_type_supported::value) { 839 | #if defined(__clang__) 840 | constexpr string_view name{__PRETTY_FUNCTION__ + 31, sizeof(__PRETTY_FUNCTION__) - 34}; 841 | #elif defined(__GNUC__) 842 | constexpr string_view name{__PRETTY_FUNCTION__ + 46, sizeof(__PRETTY_FUNCTION__) - 49}; 843 | #elif defined(_MSC_VER) 844 | constexpr string_view name{__FUNCSIG__ + 63, sizeof(__FUNCSIG__) - 81 - (__FUNCSIG__[sizeof(__FUNCSIG__) - 19] == ' ' ? 1 : 0)}; 845 | #else 846 | constexpr auto name = string_view{}; 847 | #endif 848 | return cstring{name}; 849 | } else { 850 | return cstring{custom_name}; 851 | } 852 | } 853 | 854 | template 855 | inline constexpr auto type_name_v = n(); 856 | 857 | #if __has_include() 858 | template 859 | string nameof_type_rtti(const char* tn) { 860 | static_assert(nameof_type_rtti_supported::value, "nameof::nameof_type_rtti unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); 861 | const auto dmg = abi::__cxa_demangle(tn, nullptr, nullptr, nullptr); 862 | const auto name = string{dmg}; 863 | free(dmg); 864 | assert(!name.empty() && "Type does not have a name."); 865 | 866 | return name; 867 | } 868 | 869 | template 870 | string nameof_full_type_rtti(const char* tn) { 871 | static_assert(nameof_type_rtti_supported::value, "nameof::nameof_type_rtti unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); 872 | const auto dmg = abi::__cxa_demangle(tn, nullptr, nullptr, nullptr); 873 | auto name = string{dmg}; 874 | free(dmg); 875 | assert(!name.empty() && "Type does not have a name."); 876 | if constexpr (std::is_const_v>) { 877 | name = string{"const "}.append(name); 878 | } 879 | if constexpr (std::is_volatile_v>) { 880 | name = string{"volatile "}.append(name); 881 | } 882 | if constexpr (std::is_lvalue_reference_v) { 883 | name.append(1, '&'); 884 | } 885 | if constexpr (std::is_rvalue_reference_v) { 886 | name.append("&&"); 887 | } 888 | 889 | return name; 890 | } 891 | 892 | template = 0> 893 | string nameof_short_type_rtti(const char* tn) { 894 | static_assert(nameof_type_rtti_supported::value, "nameof::nameof_type_rtti unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); 895 | const auto dmg = abi::__cxa_demangle(tn, nullptr, nullptr, nullptr); 896 | const auto pname = pretty_name(dmg); 897 | const auto name = string{pname.data(), pname.size()}; 898 | free(dmg); 899 | assert(!name.empty() && "Type does not have a short name."); 900 | 901 | return name; 902 | } 903 | #else 904 | template 905 | string nameof_type_rtti(const char* tn) { 906 | static_assert(nameof_type_rtti_supported::value, "nameof::nameof_type_rtti unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); 907 | const auto name = string_view{tn}; 908 | assert(!name.empty() && "Type does not have a name."); 909 | return {name.data(), name.size()}; 910 | } 911 | 912 | template 913 | string nameof_full_type_rtti(const char* tn) { 914 | static_assert(nameof_type_rtti_supported::value, "nameof::nameof_type_rtti unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); 915 | auto name = string{tn}; 916 | assert(!name.empty() && "Type does not have a name."); 917 | if constexpr (std::is_const_v>) { 918 | name = string{"const "}.append(name); 919 | } 920 | if constexpr (std::is_volatile_v>) { 921 | name = string{"volatile "}.append(name); 922 | } 923 | if constexpr (std::is_lvalue_reference_v) { 924 | name.append(1, '&'); 925 | } 926 | if constexpr (std::is_rvalue_reference_v) { 927 | name.append("&&"); 928 | } 929 | return name; 930 | } 931 | 932 | template = 0> 933 | string nameof_short_type_rtti(const char* tn) { 934 | static_assert(nameof_type_rtti_supported::value, "nameof::nameof_type_rtti unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); 935 | const auto name = pretty_name(tn); 936 | assert(!name.empty() && "Type does not have a short name."); 937 | return {name.data(), name.size()}; 938 | } 939 | #endif 940 | 941 | template 942 | constexpr auto n() noexcept { 943 | [[maybe_unused]] constexpr auto custom_name = customize::member_name(); 944 | 945 | if constexpr (custom_name.empty() && nameof_member_supported::value) { 946 | #if defined(__clang__) || defined(__GNUC__) 947 | constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); 948 | #elif defined(_MSC_VER) && defined(_MSVC_LANG) && _MSVC_LANG >= 202002L 949 | constexpr auto name = pretty_name({__FUNCSIG__, 950 | sizeof(__FUNCSIG__) - 18 + std::is_member_function_pointer_v}); 951 | #else 952 | constexpr auto name = string_view{}; 953 | #endif 954 | return cstring{name}; 955 | } else { 956 | return cstring{custom_name}; 957 | } 958 | } 959 | 960 | #if defined(__clang__) || defined(__GNUC__) 961 | template 962 | inline constexpr auto member_name_v = n(); 963 | #elif defined(_MSC_VER) && defined(_MSVC_LANG) && _MSVC_LANG >= 202002L 964 | template 965 | From get_base_type(Type From::*); 966 | 967 | template 968 | extern T nonexist_object; 969 | 970 | template 971 | struct Store { 972 | T v; 973 | }; 974 | 975 | template 976 | Store(T) -> Store; 977 | 978 | template 979 | consteval auto get_member_name() noexcept { 980 | if constexpr (std::is_member_function_pointer_v) { 981 | return n(); 982 | } else { 983 | constexpr bool is_defined = sizeof(decltype(get_base_type(V))) != 0; 984 | static_assert(is_defined, "nameof::nameof_member member name can use only if the struct is already fully defined. Please use NAMEOF macro, or separate definition and declaration."); 985 | if constexpr (is_defined) { 986 | return n.*V)}>(); 987 | } else { 988 | return ""; 989 | } 990 | } 991 | } 992 | 993 | template 994 | inline constexpr auto member_name_v = get_member_name(); 995 | #else 996 | template 997 | inline constexpr auto member_name_v = cstring<0>{}; 998 | #endif 999 | 1000 | template 1001 | struct is_same : std::false_type {}; 1002 | 1003 | template 1004 | struct is_same : std::true_type {}; 1005 | 1006 | template 1007 | constexpr bool is_nullptr_v = is_same>(nullptr)>::value; 1008 | 1009 | template 1010 | constexpr auto p() noexcept { 1011 | [[maybe_unused]] constexpr auto custom_name = customize::pointer_name().empty() && is_nullptr_v ? "nullptr" : customize::pointer_name(); 1012 | 1013 | if constexpr (custom_name.empty() && nameof_pointer_supported::value) { 1014 | #if defined(__clang__) 1015 | constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); 1016 | #elif defined(__GNUC__) 1017 | constexpr bool has_parenthesis = __PRETTY_FUNCTION__[sizeof(__PRETTY_FUNCTION__) - 3] == ')'; 1018 | constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2 - has_parenthesis}); 1019 | #elif defined(_MSC_VER) && defined(_MSVC_LANG) && _MSVC_LANG >= 202002L 1020 | constexpr auto name = pretty_name({__FUNCSIG__, sizeof(__FUNCSIG__) - 17}); 1021 | #else 1022 | constexpr auto name = string_view{}; 1023 | #endif 1024 | return cstring{name}; 1025 | } else { 1026 | return cstring{custom_name}; 1027 | } 1028 | } 1029 | 1030 | template 1031 | inline constexpr auto pointer_name_v = p(); 1032 | 1033 | } // namespace nameof::detail 1034 | 1035 | // Checks is nameof_type supported compiler. 1036 | inline constexpr bool is_nameof_type_supported = detail::nameof_type_supported::value; 1037 | 1038 | // Checks is nameof_type_rtti supported compiler. 1039 | inline constexpr bool is_nameof_type_rtti_supported = detail::nameof_type_rtti_supported::value; 1040 | 1041 | // Checks is nameof_member supported compiler. 1042 | inline constexpr bool is_nameof_member_supported = detail::nameof_member_supported::value; 1043 | 1044 | // Checks is nameof_pointer supported compiler. 1045 | inline constexpr bool is_nameof_pointer_supported = detail::nameof_pointer_supported::value; 1046 | 1047 | // Checks is nameof_enum supported compiler. 1048 | inline constexpr bool is_nameof_enum_supported = detail::nameof_enum_supported::value; 1049 | 1050 | // Obtains name of enum variable. 1051 | template 1052 | [[nodiscard]] constexpr auto nameof_enum(E value) noexcept -> detail::enable_if_enum_t { 1053 | using D = std::decay_t; 1054 | using U = std::underlying_type_t; 1055 | static_assert(detail::nameof_enum_supported::value, "nameof::nameof_enum unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); 1056 | static_assert(detail::count_v > 0, "nameof::nameof_enum requires enum implementation and valid max and min."); 1057 | 1058 | if constexpr (detail::is_sparse_v) { 1059 | for (std::size_t i = 0; i < detail::count_v; ++i) { 1060 | if (detail::enum_value(i) == value) { 1061 | return detail::names_v[i]; 1062 | } 1063 | } 1064 | } else { 1065 | const auto v = static_cast(value); 1066 | if (v >= detail::min_v && v <= detail::max_v) { 1067 | return detail::names_v[static_cast(v - detail::min_v)]; 1068 | } 1069 | } 1070 | return {}; // Value out of range. 1071 | } 1072 | 1073 | // Obtains name of enum variable or default value if enum variable out of range. 1074 | template 1075 | [[nodiscard]] auto nameof_enum_or(E value, string_view default_value) -> detail::enable_if_enum_t { 1076 | using D = std::decay_t; 1077 | static_assert(detail::nameof_enum_supported::value, "nameof::nameof_enum_or unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); 1078 | static_assert(detail::count_v > 0, "nameof::nameof_enum_or requires enum implementation and valid max and min."); 1079 | 1080 | if (auto v = nameof_enum(value); !v.empty()) { 1081 | return string{v.data(), v.size()}; 1082 | } 1083 | return string{default_value.data(), default_value.size()}; 1084 | } 1085 | 1086 | // Obtains name of enum-flags variable. 1087 | template 1088 | [[nodiscard]] auto nameof_enum_flag(E value, char sep = '|') -> detail::enable_if_enum_t { 1089 | using D = std::decay_t; 1090 | using U = std::underlying_type_t; 1091 | static_assert(detail::nameof_enum_supported::value, "nameof::nameof_enum_flag unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); 1092 | static_assert(detail::count_v > 0, "nameof::nameof_enum_flag requires enum-flags implementation."); 1093 | 1094 | string name; 1095 | auto check_value = U{0}; 1096 | for (std::size_t i = 0; i < detail::count_v; ++i) { 1097 | if (const auto v = static_cast(detail::enum_value(i)); (static_cast(value) & v) != 0) { 1098 | if (const auto n = detail::names_v[i]; !n.empty()) { 1099 | check_value |= v; 1100 | if (!name.empty()) { 1101 | name.append(1, sep); 1102 | } 1103 | name.append(n.data(), n.size()); 1104 | } else { 1105 | return {}; // Value out of range. 1106 | } 1107 | } 1108 | } 1109 | 1110 | if (check_value != 0 && check_value == static_cast(value)) { 1111 | return name; 1112 | } 1113 | return {}; // Invalid value or out of range. 1114 | } 1115 | 1116 | // Obtains name of static storage enum variable. 1117 | // This version is much lighter on the compile times and is not restricted to the enum_range limitation. 1118 | template = 0> 1119 | [[nodiscard]] constexpr auto nameof_enum() noexcept { 1120 | using D = std::decay_t; 1121 | static_assert(std::is_enum_v, "nameof::nameof_enum requires member enum type."); 1122 | static_assert(detail::nameof_enum_supported::value, "nameof::nameof_enum unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); 1123 | return detail::enum_name_v; 1124 | } 1125 | 1126 | // Obtains name of type, reference and cv-qualifiers are ignored. 1127 | template 1128 | [[nodiscard]] constexpr string_view nameof_type() noexcept { 1129 | using U = detail::identity>; 1130 | static_assert(detail::nameof_type_supported::value, "nameof::nameof_type unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); 1131 | return detail::type_name_v; 1132 | } 1133 | 1134 | // Obtains full name of type, with reference and cv-qualifiers. 1135 | template 1136 | [[nodiscard]] constexpr string_view nameof_full_type() noexcept { 1137 | using U = detail::identity; 1138 | static_assert(detail::nameof_type_supported::value, "nameof::nameof_type unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); 1139 | return detail::type_name_v; 1140 | } 1141 | 1142 | // Obtains short name of type. 1143 | template = 0> 1144 | [[nodiscard]] constexpr auto nameof_short_type() noexcept { 1145 | using U = detail::identity>; 1146 | static_assert(detail::nameof_type_supported::value, "nameof::nameof_type unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); 1147 | static_assert(!std::is_array_v && !std::is_pointer_v, "nameof::nameof_member requires non array and non pointer type."); 1148 | constexpr string_view name = detail::pretty_name(detail::type_name_v); 1149 | static_assert(!name.empty(), "Type does not have a short name."); 1150 | return cstring{name}; 1151 | } 1152 | 1153 | // Obtains name of member. 1154 | template , int> = 0> 1155 | [[nodiscard]] constexpr auto nameof_member() noexcept { 1156 | using U = decltype(V); 1157 | static_assert(std::is_member_pointer_v, "nameof::nameof_member requires member pointer type."); 1158 | static_assert(detail::nameof_member_supported::value, "nameof::nameof_member unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); 1159 | return detail::member_name_v; 1160 | } 1161 | 1162 | // Obtains name of a function, a global or class static variable. 1163 | template , int> = 0> 1164 | [[nodiscard]] constexpr auto nameof_pointer() noexcept { 1165 | using U = decltype(V); 1166 | static_assert(std::is_pointer_v, "nameof::nameof_pointer requires pointer type."); 1167 | static_assert(detail::nameof_pointer_supported::value, "nameof::nameof_pointer unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); 1168 | return detail::pointer_name_v; 1169 | } 1170 | 1171 | } // namespace nameof 1172 | 1173 | // Obtains name of variable, function, macro. 1174 | #define NAMEOF(...) []() constexpr noexcept { \ 1175 | ::std::void_t(); \ 1176 | constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__); \ 1177 | static_assert(!_name.empty(), "Expression does not have a name."); \ 1178 | constexpr auto _size = _name.size(); \ 1179 | constexpr auto _nameof = ::nameof::cstring<_size>{_name}; \ 1180 | return _nameof; }() 1181 | 1182 | // Obtains full name of variable, function, macro. 1183 | #define NAMEOF_FULL(...) []() constexpr noexcept { \ 1184 | ::std::void_t(); \ 1185 | constexpr auto _name = ::nameof::detail::pretty_name(#__VA_ARGS__, false); \ 1186 | static_assert(!_name.empty(), "Expression does not have a name."); \ 1187 | constexpr auto _size = _name.size(); \ 1188 | constexpr auto _nameof_full = ::nameof::cstring<_size>{_name}; \ 1189 | return _nameof_full; }() 1190 | 1191 | // Obtains raw name of variable, function, macro. 1192 | #define NAMEOF_RAW(...) []() constexpr noexcept { \ 1193 | ::std::void_t(); \ 1194 | constexpr auto _name = ::nameof::string_view{#__VA_ARGS__}; \ 1195 | static_assert(!_name.empty(), "Expression does not have a name."); \ 1196 | constexpr auto _size = _name.size(); \ 1197 | constexpr auto _nameof_raw = ::nameof::cstring<_size>{_name}; \ 1198 | return _nameof_raw; }() 1199 | 1200 | // Obtains name of enum variable. 1201 | #define NAMEOF_ENUM(...) ::nameof::nameof_enum<::std::decay_t>(__VA_ARGS__) 1202 | 1203 | // Obtains name of enum variable or default value if enum variable out of range. 1204 | #define NAMEOF_ENUM_OR(...) ::nameof::nameof_enum_or(__VA_ARGS__) 1205 | 1206 | // Obtains name of static storage enum variable. 1207 | // This version is much lighter on the compile times and is not restricted to the enum_range limitation. 1208 | #define NAMEOF_ENUM_CONST(...) ::nameof::nameof_enum<__VA_ARGS__>() 1209 | 1210 | // Obtains name of enum-flags variable. 1211 | #define NAMEOF_ENUM_FLAG(...) ::nameof::nameof_enum_flag<::std::decay_t>(__VA_ARGS__) 1212 | 1213 | // Obtains type name, reference and cv-qualifiers are ignored. 1214 | #define NAMEOF_TYPE(...) ::nameof::nameof_type<__VA_ARGS__>() 1215 | 1216 | // Obtains full type name, with reference and cv-qualifiers. 1217 | #define NAMEOF_FULL_TYPE(...) ::nameof::nameof_full_type<__VA_ARGS__>() 1218 | 1219 | // Obtains short type name. 1220 | #define NAMEOF_SHORT_TYPE(...) ::nameof::nameof_short_type<__VA_ARGS__>() 1221 | 1222 | // Obtains type name of expression, reference and cv-qualifiers are ignored. 1223 | #define NAMEOF_TYPE_EXPR(...) ::nameof::nameof_type() 1224 | 1225 | // Obtains full type name of expression, with reference and cv-qualifiers. 1226 | #define NAMEOF_FULL_TYPE_EXPR(...) ::nameof::nameof_full_type() 1227 | 1228 | // Obtains short type name of expression. 1229 | #define NAMEOF_SHORT_TYPE_EXPR(...) ::nameof::nameof_short_type() 1230 | 1231 | // Obtains type name, with reference and cv-qualifiers, using RTTI. 1232 | #define NAMEOF_TYPE_RTTI(...) ::nameof::detail::nameof_type_rtti<::std::void_t>(typeid(__VA_ARGS__).name()) 1233 | 1234 | // Obtains full type name, using RTTI. 1235 | #define NAMEOF_FULL_TYPE_RTTI(...) ::nameof::detail::nameof_full_type_rtti(typeid(__VA_ARGS__).name()) 1236 | 1237 | // Obtains short type name, using RTTI. 1238 | #define NAMEOF_SHORT_TYPE_RTTI(...) ::nameof::detail::nameof_short_type_rtti(typeid(__VA_ARGS__).name()) 1239 | 1240 | // Obtains name of member. 1241 | #define NAMEOF_MEMBER(...) ::nameof::nameof_member<__VA_ARGS__>() 1242 | 1243 | // Obtains name of a function, a global or class static variable. 1244 | #define NAMEOF_POINTER(...) ::nameof::nameof_pointer<__VA_ARGS__>() 1245 | 1246 | #if defined(__clang__) 1247 | # pragma clang diagnostic pop 1248 | #elif defined(__GNUC__) 1249 | # pragma GCC diagnostic pop 1250 | #elif defined(_MSC_VER) 1251 | # pragma warning(pop) 1252 | #endif 1253 | 1254 | #endif // NEARGYE_NAMEOF_HPP 1255 | -------------------------------------------------------------------------------- /test/3rdparty/Catch2/LICENSE: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(CheckCXXCompilerFlag) 2 | 3 | if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 4 | set(OPTIONS /W4 /WX) 5 | check_cxx_compiler_flag(/permissive HAS_PERMISSIVE_FLAG) 6 | if(HAS_PERMISSIVE_FLAG) 7 | set(OPTIONS ${OPTIONS} /permissive-) 8 | endif() 9 | 10 | check_cxx_compiler_flag(/std:c++20 HAS_CPP20_FLAG) 11 | check_cxx_compiler_flag(/std:c++23 HAS_CPP23_FLAG) 12 | check_cxx_compiler_flag(/std:c++latest HAS_CPPLATEST_FLAG) 13 | elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") 14 | set(CMAKE_VERBOSE_MAKEFILE ON) 15 | set(OPTIONS -Wall -Wextra -pedantic-errors -Werror) 16 | 17 | check_cxx_compiler_flag(-std=c++20 HAS_CPP20_FLAG) 18 | check_cxx_compiler_flag(-std=c++23 HAS_CPP23_FLAG) 19 | endif() 20 | 21 | function(make_test src target std) 22 | add_executable(${target} ${src}) 23 | target_compile_options(${target} PRIVATE ${OPTIONS}) 24 | target_include_directories(${target} PRIVATE 3rdparty/Catch2) 25 | target_link_libraries(${target} PRIVATE ${CMAKE_PROJECT_NAME}) 26 | set_target_properties(${target} PROPERTIES CXX_EXTENSIONS OFF) 27 | if(std) 28 | if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 29 | target_compile_options(${target} PRIVATE /std:${std}) 30 | else() 31 | target_compile_options(${target} PRIVATE -std=${std}) 32 | endif() 33 | endif() 34 | add_test(NAME ${target} COMMAND ${target}) 35 | endfunction() 36 | 37 | make_test(test.cpp test-cpp17 c++17) 38 | make_test(test_aliases.cpp test_aliases-cpp17 c++17) 39 | 40 | if(HAS_CPP20_FLAG) 41 | make_test(test.cpp test-cpp20 c++20) 42 | make_test(test_aliases.cpp test_aliases-cpp20 c++20) 43 | endif() 44 | 45 | if(HAS_CPP23_FLAG) 46 | make_test(test.cpp test-cpp23 c++23) 47 | make_test(test_aliases.cpp test_aliases-cpp23 c++23) 48 | endif() 49 | 50 | if(HAS_CPPLATEST_FLAG) 51 | make_test(test.cpp test-cpplatest c++latest) 52 | make_test(test_aliases.cpp test_aliases-cpplatest c++latest) 53 | endif() 54 | -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | // Licensed under the MIT License . 2 | // SPDX-License-Identifier: MIT 3 | // Copyright (c) 2018 - 2024 Daniil Goncharov . 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #define CATCH_CONFIG_MAIN 24 | #include 25 | 26 | #define NAMEOF_ENUM_RANGE_MIN -120 27 | #define NAMEOF_ENUM_RANGE_MAX 120 28 | #include 29 | 30 | #include 31 | #include 32 | 33 | #ifdef NDEBUG 34 | # define NAMEOF_DEBUG_REQUIRE(...) REQUIRE(__VA_ARGS__) 35 | #else 36 | # define NAMEOF_DEBUG_REQUIRE(...) 37 | #endif 38 | 39 | struct SomeStruct { 40 | int somefield = 0; 41 | 42 | void SomeMethod1(int) { 43 | throw std::runtime_error{"should not be called!"}; 44 | } 45 | 46 | int SomeMethod2() const { 47 | throw std::runtime_error{"should not be called!"}; 48 | } 49 | 50 | static int somestaticfield; 51 | constexpr static int someotherstaticfield = 21; 52 | }; 53 | 54 | int SomeStruct::somestaticfield; 55 | 56 | int someglobalvariable = 0; 57 | const int someglobalconstvariable = 42; 58 | 59 | void SomeMethod3() { 60 | throw std::runtime_error{"should not be called!"}; 61 | } 62 | 63 | template 64 | std::string SomeMethod4(U) { 65 | throw std::runtime_error{"should not be called!"}; 66 | } 67 | 68 | template 69 | class SomeClass { 70 | public: 71 | void SomeMethod5() const { 72 | throw std::runtime_error{"should not be called!"}; 73 | } 74 | 75 | template 76 | C SomeMethod6() const { 77 | throw std::runtime_error{"should not be called!"}; 78 | } 79 | }; 80 | 81 | struct Long { 82 | struct LL { 83 | int field = 0; 84 | }; 85 | LL ll; 86 | }; 87 | 88 | enum class Color { RED = -12, GREEN = 7, BLUE = 15 }; 89 | 90 | enum class Numbers : int { one = 1, two, three, many = 127 }; 91 | 92 | enum Directions { Up = 85, Down = -42, Right = 120, Left = -120 }; 93 | 94 | enum number : unsigned long { one = 100, two = 200, three = 300, four = 400 }; 95 | 96 | enum AnimalFlags { 97 | HasClaws = 1, 98 | CanFly = 2, 99 | EatsFish = 4, 100 | Endangered = 8, 101 | }; 102 | 103 | enum class BigFlags : std::uint64_t { 104 | A = 1, 105 | B = (static_cast(0x1) << 20), 106 | C = (static_cast(0x1) << 40), 107 | D = (static_cast(0x1) << 63), 108 | }; 109 | 110 | template <> 111 | struct nameof::customize::enum_range { 112 | static_assert(std::is_enum_v, "nameof::enum_range requires enum type."); 113 | static constexpr int min = 100; 114 | static constexpr int max = 300; 115 | static_assert(max > min, "nameof::enum_range requires max > min."); 116 | }; 117 | 118 | enum class OutOfRange { 119 | too_low = NAMEOF_ENUM_RANGE_MIN - 1, 120 | required_to_work = 0, 121 | too_high = NAMEOF_ENUM_RANGE_MAX + 1 122 | }; 123 | 124 | struct TestRtti { 125 | struct Base { virtual ~Base() = default; }; 126 | struct Derived : Base {}; 127 | }; 128 | 129 | SomeStruct struct_var; 130 | Long othervar; 131 | SomeStruct* ptr_s = &struct_var; 132 | SomeStruct& ref_s = struct_var; 133 | 134 | SomeClass class_var; 135 | const SomeClass volatile * ptr_c = nullptr; 136 | 137 | const Color color = Color::RED; 138 | 139 | TEST_CASE("NAMEOF") { 140 | SECTION("variable") { 141 | constexpr auto name = NAMEOF(othervar); 142 | REQUIRE(name == "othervar"); 143 | REQUIRE(NAMEOF(struct_var) == "struct_var"); 144 | REQUIRE(NAMEOF(::struct_var) == "struct_var"); 145 | REQUIRE(NAMEOF(ptr_s) == "ptr_s"); 146 | REQUIRE(NAMEOF(color) == "color"); 147 | } 148 | 149 | SECTION("member") { 150 | REQUIRE(NAMEOF(struct_var.somefield) == "somefield"); 151 | REQUIRE(NAMEOF((&struct_var)->somefield) == "somefield"); 152 | REQUIRE(NAMEOF(othervar.ll.field) == "field"); 153 | } 154 | 155 | SECTION("function") { 156 | REQUIRE(NAMEOF(&SomeStruct::SomeMethod1) == "SomeMethod1"); 157 | REQUIRE(NAMEOF(struct_var.SomeMethod1(1)) == "SomeMethod1"); 158 | REQUIRE(NAMEOF(&SomeStruct::SomeMethod2) == "SomeMethod2"); 159 | REQUIRE(NAMEOF(struct_var.SomeMethod2()) == "SomeMethod2"); 160 | REQUIRE(NAMEOF(SomeMethod3) == "SomeMethod3"); 161 | REQUIRE(NAMEOF(SomeMethod3()) == "SomeMethod3"); 162 | REQUIRE(NAMEOF(SomeMethod4) == "SomeMethod4"); 163 | REQUIRE(NAMEOF(SomeMethod4(1.0f)) == "SomeMethod4"); 164 | REQUIRE(NAMEOF(&SomeClass::SomeMethod5) == "SomeMethod5"); 165 | REQUIRE(NAMEOF(class_var.SomeMethod5()) == "SomeMethod5"); 166 | REQUIRE(NAMEOF(&SomeClass::SomeMethod6) == "SomeMethod6"); 167 | REQUIRE(NAMEOF(class_var.SomeMethod6()) == "SomeMethod6"); 168 | } 169 | 170 | SECTION("enum") { 171 | REQUIRE(NAMEOF(Color::RED) == "RED"); 172 | REQUIRE(NAMEOF(Color::BLUE) == "BLUE"); 173 | } 174 | } 175 | 176 | TEST_CASE("NAMEOF_FULL") { 177 | SECTION("variable") { 178 | constexpr auto full_name = NAMEOF_FULL(othervar); 179 | REQUIRE(full_name == "othervar"); 180 | REQUIRE(NAMEOF_FULL(struct_var) == "struct_var"); 181 | REQUIRE(NAMEOF_FULL(::struct_var) == "struct_var"); 182 | REQUIRE(NAMEOF_FULL(ptr_s) == "ptr_s"); 183 | REQUIRE(NAMEOF_FULL(color) == "color"); 184 | } 185 | 186 | SECTION("member") { 187 | REQUIRE(NAMEOF_FULL(struct_var.somefield) == "somefield"); 188 | REQUIRE(NAMEOF_FULL((&struct_var)->somefield) == "somefield"); 189 | REQUIRE(NAMEOF_FULL(othervar.ll.field) == "field"); 190 | } 191 | 192 | SECTION("function") { 193 | REQUIRE(NAMEOF_FULL(&SomeStruct::SomeMethod1) == "SomeMethod1"); 194 | REQUIRE(NAMEOF_FULL(struct_var.SomeMethod1(1)) == "SomeMethod1"); 195 | REQUIRE(NAMEOF_FULL(&SomeStruct::SomeMethod2) == "SomeMethod2"); 196 | REQUIRE(NAMEOF_FULL(struct_var.SomeMethod2()) == "SomeMethod2"); 197 | REQUIRE(NAMEOF_FULL(SomeMethod3) == "SomeMethod3"); 198 | REQUIRE(NAMEOF_FULL(SomeMethod3()) == "SomeMethod3"); 199 | REQUIRE(NAMEOF_FULL(SomeMethod4) == "SomeMethod4"); 200 | REQUIRE(NAMEOF_FULL(SomeMethod4(1.0f)) == "SomeMethod4"); 201 | REQUIRE(NAMEOF_FULL(&SomeClass::SomeMethod5) == "SomeMethod5"); 202 | REQUIRE(NAMEOF_FULL(class_var.SomeMethod5()) == "SomeMethod5"); 203 | REQUIRE(NAMEOF_FULL(&SomeClass::SomeMethod6) == "SomeMethod6"); 204 | REQUIRE(NAMEOF_FULL(class_var.SomeMethod6()) == "SomeMethod6"); 205 | } 206 | 207 | SECTION("enum") { 208 | REQUIRE(NAMEOF_FULL(Color::RED) == "RED"); 209 | REQUIRE(NAMEOF_FULL(Color::BLUE) == "BLUE"); 210 | } 211 | } 212 | 213 | TEST_CASE("NAMEOF_RAW") { 214 | SECTION("variable") { 215 | constexpr auto raw_name = NAMEOF_RAW(othervar); 216 | REQUIRE(raw_name == "othervar"); 217 | REQUIRE(NAMEOF_RAW(struct_var) == "struct_var"); 218 | REQUIRE(NAMEOF_RAW(&struct_var) == "&struct_var"); 219 | REQUIRE(NAMEOF_RAW(::struct_var) == "::struct_var"); 220 | REQUIRE(NAMEOF_RAW(ptr_s) == "ptr_s"); 221 | REQUIRE(NAMEOF_RAW(*ptr_s) == "*ptr_s"); 222 | REQUIRE(NAMEOF_RAW(ptr_s[0]) == "ptr_s[0]"); 223 | REQUIRE(NAMEOF_RAW(color) == "color"); 224 | } 225 | 226 | SECTION("member") { 227 | REQUIRE(NAMEOF_RAW(struct_var.somefield) == "struct_var.somefield"); 228 | REQUIRE(NAMEOF_RAW(struct_var.somefield++) == "struct_var.somefield++"); 229 | REQUIRE(NAMEOF_RAW((&struct_var)->somefield) == "(&struct_var)->somefield"); 230 | REQUIRE(NAMEOF_RAW(othervar.ll.field) == "othervar.ll.field"); 231 | REQUIRE(NAMEOF_RAW(+struct_var.somefield) == "+struct_var.somefield"); 232 | REQUIRE(NAMEOF_RAW(-struct_var.somefield) == "-struct_var.somefield"); 233 | REQUIRE(NAMEOF_RAW(~struct_var.somefield) == "~struct_var.somefield"); 234 | REQUIRE(NAMEOF_RAW(!struct_var.somefield) == "!struct_var.somefield"); 235 | REQUIRE(NAMEOF_RAW(struct_var.somefield + ref_s.somefield) == "struct_var.somefield + ref_s.somefield"); 236 | } 237 | 238 | SECTION("function") { 239 | REQUIRE(NAMEOF_RAW(&SomeStruct::SomeMethod1) == "&SomeStruct::SomeMethod1"); 240 | REQUIRE(NAMEOF_RAW(struct_var.SomeMethod1(1)) == "struct_var.SomeMethod1(1)"); 241 | REQUIRE(NAMEOF_RAW(&SomeStruct::SomeMethod2) == "&SomeStruct::SomeMethod2"); 242 | REQUIRE(NAMEOF_RAW(struct_var.SomeMethod2()) == "struct_var.SomeMethod2()"); 243 | REQUIRE(NAMEOF_RAW(SomeMethod3) == "SomeMethod3"); 244 | REQUIRE(NAMEOF_RAW(SomeMethod3()) == "SomeMethod3()"); 245 | REQUIRE(NAMEOF_RAW(SomeMethod4) == "SomeMethod4"); 246 | REQUIRE(NAMEOF_RAW(SomeMethod4(1.0f)) == "SomeMethod4(1.0f)"); 247 | REQUIRE(NAMEOF_RAW(&SomeClass::SomeMethod5) == "&SomeClass::SomeMethod5"); 248 | REQUIRE(NAMEOF_RAW(class_var.SomeMethod5()) == "class_var.SomeMethod5()"); 249 | REQUIRE(NAMEOF_RAW(&SomeClass::SomeMethod6) == "&SomeClass::SomeMethod6"); 250 | REQUIRE(NAMEOF_RAW(class_var.SomeMethod6()) == "class_var.SomeMethod6()"); 251 | } 252 | 253 | SECTION("enum") { 254 | REQUIRE(NAMEOF_RAW(Color::RED) == "Color::RED"); 255 | REQUIRE(NAMEOF_RAW(Color::BLUE) == "Color::BLUE"); 256 | } 257 | 258 | SECTION("macro") { 259 | REQUIRE(NAMEOF_RAW(__cplusplus) == "__cplusplus"); 260 | REQUIRE(NAMEOF_RAW(__LINE__) == "__LINE__"); 261 | REQUIRE(NAMEOF_RAW(__FILE__) == "__FILE__"); 262 | } 263 | } 264 | 265 | #if defined(NAMEOF_ENUM_SUPPORTED) 266 | 267 | static_assert(nameof::is_nameof_enum_supported, "nameof::nameof_enum: Unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); 268 | 269 | TEST_CASE("nameof_enum") { 270 | SECTION("automatic storage") { 271 | constexpr Color cr = Color::RED; 272 | constexpr auto cr_name = nameof::nameof_enum(cr); 273 | Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; 274 | REQUIRE(cr_name == "RED"); 275 | REQUIRE(nameof::nameof_enum(Color::BLUE) == "BLUE"); 276 | REQUIRE(nameof::nameof_enum(cm[1]) == "GREEN"); 277 | NAMEOF_DEBUG_REQUIRE(nameof::nameof_enum(static_cast(0)).empty()); 278 | 279 | constexpr Numbers no = Numbers::one; 280 | constexpr auto no_name = nameof::nameof_enum(no); 281 | REQUIRE(no_name == "one"); 282 | REQUIRE(nameof::nameof_enum(Numbers::two) == "two"); 283 | REQUIRE(nameof::nameof_enum(Numbers::three) == "three"); 284 | NAMEOF_DEBUG_REQUIRE(nameof::nameof_enum(Numbers::many).empty()); 285 | NAMEOF_DEBUG_REQUIRE(nameof::nameof_enum(static_cast(0)).empty()); 286 | 287 | constexpr Directions dr = Directions::Right; 288 | constexpr auto dr_name = nameof::nameof_enum(dr); 289 | REQUIRE(nameof::nameof_enum(Directions::Up) == "Up"); 290 | REQUIRE(nameof::nameof_enum(Directions::Down) == "Down"); 291 | REQUIRE(dr_name == "Right"); 292 | REQUIRE(nameof::nameof_enum(Directions::Left) == "Left"); 293 | NAMEOF_DEBUG_REQUIRE(nameof::nameof_enum(static_cast(0)).empty()); 294 | 295 | constexpr number nt = number::three; 296 | constexpr auto nt_name = nameof::nameof_enum(nt); 297 | REQUIRE(nameof::nameof_enum(number::one) == "one"); 298 | REQUIRE(nameof::nameof_enum(number::two) == "two"); 299 | REQUIRE(nt_name == "three"); 300 | NAMEOF_DEBUG_REQUIRE(nameof::nameof_enum(number::four).empty()); 301 | NAMEOF_DEBUG_REQUIRE(nameof::nameof_enum(static_cast(0)).empty()); 302 | } 303 | 304 | SECTION("static storage") { 305 | constexpr Color cr = Color::RED; 306 | constexpr auto cr_name = nameof::nameof_enum(); 307 | constexpr Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; 308 | REQUIRE(cr_name == "RED"); 309 | REQUIRE(nameof::nameof_enum() == "BLUE"); 310 | REQUIRE(nameof::nameof_enum() == "GREEN"); 311 | 312 | constexpr Numbers no = Numbers::one; 313 | constexpr auto no_name = nameof::nameof_enum(); 314 | REQUIRE(no_name == "one"); 315 | REQUIRE(nameof::nameof_enum() == "two"); 316 | REQUIRE(nameof::nameof_enum() == "three"); 317 | REQUIRE(nameof::nameof_enum() == "many"); 318 | 319 | constexpr Directions dr = Directions::Right; 320 | constexpr auto dr_name = nameof::nameof_enum(); 321 | REQUIRE(nameof::nameof_enum() == "Up"); 322 | REQUIRE(nameof::nameof_enum() == "Down"); 323 | REQUIRE(dr_name == "Right"); 324 | REQUIRE(nameof::nameof_enum() == "Left"); 325 | 326 | constexpr number nt = number::three; 327 | constexpr auto nt_name = nameof::nameof_enum(); 328 | REQUIRE(nameof::nameof_enum() == "one"); 329 | REQUIRE(nameof::nameof_enum() == "two"); 330 | REQUIRE(nt_name == "three"); 331 | REQUIRE(nameof::nameof_enum() == "four"); 332 | } 333 | } 334 | 335 | TEST_CASE("nameof_enum_flag") { 336 | constexpr AnimalFlags af = AnimalFlags::HasClaws; 337 | auto af_name = nameof::nameof_enum_flag(af); 338 | AnimalFlags afm[3] = {AnimalFlags::HasClaws, AnimalFlags::CanFly, AnimalFlags::EatsFish}; 339 | REQUIRE(af_name == "HasClaws"); 340 | REQUIRE(nameof::nameof_enum_flag(AnimalFlags::EatsFish) == "EatsFish"); 341 | REQUIRE(nameof::nameof_enum_flag(afm[1]) == "CanFly"); 342 | NAMEOF_DEBUG_REQUIRE(nameof::nameof_enum_flag(static_cast(0)).empty()); 343 | REQUIRE(nameof::nameof_enum_flag(static_cast(1 | 2)) == "HasClaws|CanFly"); 344 | REQUIRE(nameof::nameof_enum_flag(static_cast(1 | 2 | 4)) == "HasClaws|CanFly|EatsFish"); 345 | REQUIRE(nameof::nameof_enum_flag(static_cast(1 | 0 | 8)) == "HasClaws|Endangered"); 346 | NAMEOF_DEBUG_REQUIRE(nameof::nameof_enum_flag(static_cast(0)).empty()); 347 | 348 | constexpr BigFlags bf = BigFlags::A; 349 | auto bf_name = nameof::nameof_enum_flag(bf); 350 | BigFlags bfm[3] = {BigFlags::A, BigFlags::B, BigFlags::C}; 351 | REQUIRE(bf_name == "A"); 352 | REQUIRE(nameof::nameof_enum_flag(BigFlags::C) == "C"); 353 | REQUIRE(nameof::nameof_enum_flag(bfm[1]) == "B"); 354 | NAMEOF_DEBUG_REQUIRE(nameof::nameof_enum_flag(static_cast(0)).empty()); 355 | NAMEOF_DEBUG_REQUIRE(nameof::nameof_enum_flag(static_cast(1 | 2)).empty()); 356 | REQUIRE(nameof::nameof_enum_flag(static_cast(1 | (static_cast(0x1) << 20))) == "A|B"); 357 | REQUIRE(nameof::nameof_enum_flag(static_cast(1 | (static_cast(0x1) << 20) | (static_cast(0x1) << 63))) == "A|B|D"); 358 | REQUIRE(nameof::nameof_enum_flag(static_cast(1 | 0 | (static_cast(0x1) << 40))) == "A|C"); 359 | REQUIRE(nameof::nameof_enum_flag(static_cast(1 | 0 | (static_cast(0x1) << 40))) == "A|C"); 360 | REQUIRE(nameof::nameof_enum_flag(static_cast((static_cast(0x1) << 63) | 1)) == "A|D"); 361 | NAMEOF_DEBUG_REQUIRE(nameof::nameof_enum_flag(static_cast(2)).empty()); 362 | NAMEOF_DEBUG_REQUIRE(nameof::nameof_enum_flag(static_cast((static_cast(0x1) << 63) | 2)).empty()); 363 | } 364 | 365 | TEST_CASE("NAMEOF_ENUM") { 366 | constexpr Color cr = Color::RED; 367 | constexpr auto cr_name = NAMEOF_ENUM(cr); 368 | Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; 369 | REQUIRE(cr_name == "RED"); 370 | REQUIRE(NAMEOF_ENUM(Color::BLUE) == "BLUE"); 371 | REQUIRE(NAMEOF_ENUM(cm[1]) == "GREEN"); 372 | NAMEOF_DEBUG_REQUIRE(NAMEOF_ENUM(static_cast(0)).empty()); 373 | 374 | constexpr Numbers no = Numbers::one; 375 | constexpr auto no_name = NAMEOF_ENUM(no); 376 | REQUIRE(no_name == "one"); 377 | REQUIRE(NAMEOF_ENUM(Numbers::two) == "two"); 378 | REQUIRE(NAMEOF_ENUM(Numbers::three) == "three"); 379 | NAMEOF_DEBUG_REQUIRE(NAMEOF_ENUM(Numbers::many).empty()); 380 | NAMEOF_DEBUG_REQUIRE(NAMEOF_ENUM(static_cast(0)).empty()); 381 | 382 | constexpr Directions dr = Directions::Right; 383 | constexpr auto dr_name = NAMEOF_ENUM(dr); 384 | REQUIRE(NAMEOF_ENUM(Directions::Up) == "Up"); 385 | REQUIRE(NAMEOF_ENUM(Directions::Down) == "Down"); 386 | REQUIRE(dr_name == "Right"); 387 | REQUIRE(NAMEOF_ENUM(Directions::Left) == "Left"); 388 | NAMEOF_DEBUG_REQUIRE(NAMEOF_ENUM(static_cast(0)).empty()); 389 | 390 | constexpr number nt = number::three; 391 | constexpr auto nt_name = NAMEOF_ENUM(nt); 392 | REQUIRE(NAMEOF_ENUM(number::one) == "one"); 393 | REQUIRE(NAMEOF_ENUM(number::two) == "two"); 394 | REQUIRE(nt_name == "three"); 395 | NAMEOF_DEBUG_REQUIRE(NAMEOF_ENUM(number::four).empty()); 396 | NAMEOF_DEBUG_REQUIRE(NAMEOF_ENUM(static_cast(0)).empty()); 397 | } 398 | 399 | TEST_CASE("NAMEOF_ENUM_CONST") { 400 | constexpr Color cr = Color::RED; 401 | constexpr auto cr_name = NAMEOF_ENUM_CONST(cr); 402 | constexpr Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE}; 403 | REQUIRE(cr_name == "RED"); 404 | REQUIRE(NAMEOF_ENUM_CONST(Color::BLUE) == "BLUE"); 405 | REQUIRE(NAMEOF_ENUM_CONST(cm[1]) == "GREEN"); 406 | 407 | constexpr Numbers no = Numbers::one; 408 | constexpr auto no_name = NAMEOF_ENUM_CONST(no); 409 | REQUIRE(no_name == "one"); 410 | REQUIRE(NAMEOF_ENUM_CONST(Numbers::two) == "two"); 411 | REQUIRE(NAMEOF_ENUM_CONST(Numbers::three) == "three"); 412 | REQUIRE(NAMEOF_ENUM_CONST(Numbers::many) == "many"); 413 | 414 | constexpr Directions dr = Directions::Right; 415 | constexpr auto dr_name = NAMEOF_ENUM_CONST(dr); 416 | REQUIRE(NAMEOF_ENUM_CONST(Directions::Up) == "Up"); 417 | REQUIRE(NAMEOF_ENUM_CONST(Directions::Down) == "Down"); 418 | REQUIRE(dr_name == "Right"); 419 | REQUIRE(NAMEOF_ENUM_CONST(Directions::Left) == "Left"); 420 | 421 | constexpr number nt = number::three; 422 | constexpr auto nt_name = NAMEOF_ENUM_CONST(nt); 423 | REQUIRE(NAMEOF_ENUM_CONST(number::one) == "one"); 424 | REQUIRE(NAMEOF_ENUM_CONST(number::two) == "two"); 425 | REQUIRE(nt_name == "three"); 426 | REQUIRE(NAMEOF_ENUM_CONST(number::four) == "four"); 427 | } 428 | 429 | TEST_CASE("NAMEOF_ENUM_FLAG") { 430 | constexpr AnimalFlags af = AnimalFlags::HasClaws; 431 | auto af_name = NAMEOF_ENUM_FLAG(af); 432 | AnimalFlags afm[3] = {AnimalFlags::HasClaws, AnimalFlags::CanFly, AnimalFlags::EatsFish}; 433 | REQUIRE(af_name == "HasClaws"); 434 | REQUIRE(NAMEOF_ENUM_FLAG(afm[1]) == "CanFly"); 435 | REQUIRE(NAMEOF_ENUM_FLAG(AnimalFlags::EatsFish) == "EatsFish"); 436 | REQUIRE(NAMEOF_ENUM_FLAG(AnimalFlags::Endangered) == "Endangered"); 437 | NAMEOF_DEBUG_REQUIRE(NAMEOF_ENUM_FLAG(static_cast(0)).empty()); 438 | REQUIRE(NAMEOF_ENUM_FLAG(static_cast(1 | 2)) == "HasClaws|CanFly"); 439 | REQUIRE(NAMEOF_ENUM_FLAG(static_cast(1 | 2 | 4)) == "HasClaws|CanFly|EatsFish"); 440 | REQUIRE(NAMEOF_ENUM_FLAG(static_cast(1 | 0 | 8)) == "HasClaws|Endangered"); 441 | NAMEOF_DEBUG_REQUIRE(NAMEOF_ENUM_FLAG(static_cast(0)).empty()); 442 | 443 | constexpr BigFlags bf = BigFlags::A; 444 | auto bf_name = NAMEOF_ENUM_FLAG(bf); 445 | BigFlags bfm[3] = {BigFlags::A, BigFlags::B, BigFlags::C}; 446 | REQUIRE(bf_name == "A"); 447 | REQUIRE(NAMEOF_ENUM_FLAG(bfm[1]) == "B"); 448 | REQUIRE(NAMEOF_ENUM_FLAG(BigFlags::C) == "C"); 449 | REQUIRE(NAMEOF_ENUM_FLAG(BigFlags::D) == "D"); 450 | NAMEOF_DEBUG_REQUIRE(NAMEOF_ENUM_FLAG(static_cast(0)).empty()); 451 | NAMEOF_DEBUG_REQUIRE(NAMEOF_ENUM_FLAG(static_cast(1 | 2)).empty()); 452 | REQUIRE(NAMEOF_ENUM_FLAG(static_cast(1 | (static_cast(0x1) << 20))) == "A|B"); 453 | REQUIRE(NAMEOF_ENUM_FLAG(static_cast(1 | (static_cast(0x1) << 20) | (static_cast(0x1) << 63))) == "A|B|D"); 454 | REQUIRE(NAMEOF_ENUM_FLAG(static_cast((static_cast(0x1) << 63) | 1)) == "A|D"); 455 | NAMEOF_DEBUG_REQUIRE(NAMEOF_ENUM_FLAG(static_cast(2)).empty()); 456 | NAMEOF_DEBUG_REQUIRE(NAMEOF_ENUM_FLAG(static_cast((static_cast(0x1) << 63) | 2)).empty()); 457 | } 458 | 459 | TEST_CASE("nameof_enum_or") { 460 | OutOfRange low = OutOfRange::too_low; 461 | OutOfRange high = OutOfRange::too_high; 462 | auto low_name = nameof::nameof_enum_or(low, "-121"); 463 | auto high_name = nameof::nameof_enum_or(high, "121"); 464 | constexpr OutOfRange oor[] = {OutOfRange::too_high, OutOfRange::too_low}; 465 | REQUIRE(low_name == "-121"); 466 | REQUIRE(high_name == "121"); 467 | REQUIRE(nameof::nameof_enum_or(oor[0], "121") == "121"); 468 | } 469 | 470 | TEST_CASE("NAMEOF_ENUM_OR") { 471 | OutOfRange low = OutOfRange::too_low; 472 | OutOfRange high = OutOfRange::too_high; 473 | auto low_name = NAMEOF_ENUM_OR(low, "-121"); 474 | auto high_name = NAMEOF_ENUM_OR(high, "121"); 475 | constexpr OutOfRange oor[] = {OutOfRange::too_high, OutOfRange::too_low}; 476 | REQUIRE(low_name == "-121"); 477 | REQUIRE(high_name == "121"); 478 | REQUIRE(NAMEOF_ENUM_OR(oor[0], "121") == "121"); 479 | } 480 | 481 | #endif 482 | 483 | #if defined(NAMEOF_TYPE_SUPPORTED) 484 | 485 | static_assert(nameof::is_nameof_type_supported, "nameof::nameof_type: Unsupported compiler (https://github.com/Neargye/nameof#compiler-compatibility)."); 486 | 487 | TEST_CASE("nameof::nameof_type") { 488 | constexpr auto type_name = nameof::nameof_type(); 489 | #if defined(__clang__) 490 | REQUIRE(type_name == "SomeStruct"); 491 | REQUIRE(nameof::nameof_type() == "SomeStruct *"); 492 | REQUIRE(nameof::nameof_type() == "SomeStruct"); 493 | REQUIRE(nameof::nameof_type() == "SomeStruct"); 494 | REQUIRE(nameof::nameof_type() == "SomeStruct *"); 495 | REQUIRE(nameof::nameof_type() == "SomeStruct"); 496 | REQUIRE(nameof::nameof_type() == "const volatile SomeStruct *"); 497 | 498 | REQUIRE(nameof::nameof_type>() == "SomeClass"); 499 | REQUIRE(nameof::nameof_type volatile *>() == "const volatile SomeClass *"); 500 | 501 | REQUIRE(nameof::nameof_type() == "Long"); 502 | REQUIRE(nameof::nameof_type() == "Long"); 503 | REQUIRE(nameof::nameof_type() == "Long::LL"); 504 | 505 | REQUIRE(nameof::nameof_type() == "Color"); 506 | #elif defined(_MSC_VER) 507 | REQUIRE(type_name == "struct SomeStruct"); 508 | REQUIRE(nameof::nameof_type() == "struct SomeStruct *"); 509 | REQUIRE(nameof::nameof_type() == "struct SomeStruct"); 510 | REQUIRE(nameof::nameof_type() == "struct SomeStruct"); 511 | REQUIRE(nameof::nameof_type() == "struct SomeStruct *"); 512 | REQUIRE(nameof::nameof_type() == "struct SomeStruct"); 513 | REQUIRE(nameof::nameof_type() == "struct SomeStruct const volatile *"); 514 | 515 | REQUIRE(nameof::nameof_type>() == "class SomeClass"); 516 | REQUIRE(nameof::nameof_type volatile *>() == "class SomeClass const volatile *"); 517 | 518 | REQUIRE(nameof::nameof_type() == "struct Long"); 519 | REQUIRE(nameof::nameof_type() == "struct Long"); 520 | REQUIRE(nameof::nameof_type() == "struct Long::LL"); 521 | 522 | REQUIRE(nameof::nameof_type() == "enum Color"); 523 | #elif defined(__GNUC__) 524 | REQUIRE(type_name == "SomeStruct"); 525 | REQUIRE(nameof::nameof_type() == "SomeStruct*"); 526 | REQUIRE(nameof::nameof_type() == "SomeStruct"); 527 | REQUIRE(nameof::nameof_type() == "SomeStruct"); 528 | REQUIRE(nameof::nameof_type() == "SomeStruct*"); 529 | REQUIRE(nameof::nameof_type() == "SomeStruct"); 530 | REQUIRE(nameof::nameof_type() == "const volatile SomeStruct*"); 531 | 532 | REQUIRE(nameof::nameof_type>() == "SomeClass"); 533 | REQUIRE(nameof::nameof_type volatile *>() == "const volatile SomeClass*"); 534 | 535 | REQUIRE(nameof::nameof_type() == "Long"); 536 | REQUIRE(nameof::nameof_type() == "Long"); 537 | REQUIRE(nameof::nameof_type() == "Long::LL"); 538 | 539 | REQUIRE(nameof::nameof_type() == "Color"); 540 | #endif 541 | } 542 | 543 | TEST_CASE("nameof::nameof_full_type") { 544 | constexpr auto type_name = nameof::nameof_full_type(); 545 | #if defined(__clang__) 546 | REQUIRE(type_name == "SomeStruct"); 547 | REQUIRE(nameof::nameof_full_type() == "SomeStruct *"); 548 | REQUIRE(nameof::nameof_full_type() == "SomeStruct &"); 549 | REQUIRE(nameof::nameof_full_type() == "SomeStruct"); 550 | REQUIRE(nameof::nameof_full_type() == "SomeStruct *"); 551 | REQUIRE(nameof::nameof_full_type() == "SomeStruct &"); 552 | REQUIRE(nameof::nameof_full_type() == "const volatile SomeStruct *"); 553 | 554 | REQUIRE(nameof::nameof_full_type>() == "SomeClass"); 555 | REQUIRE(nameof::nameof_full_type volatile *>() == "const volatile SomeClass *"); 556 | 557 | REQUIRE(nameof::nameof_full_type() == "Long"); 558 | REQUIRE(nameof::nameof_full_type() == "Long"); 559 | REQUIRE(nameof::nameof_full_type() == "Long::LL"); 560 | 561 | REQUIRE(nameof::nameof_full_type() == "Color"); 562 | #elif defined(_MSC_VER) 563 | REQUIRE(type_name == "struct SomeStruct"); 564 | REQUIRE(nameof::nameof_full_type() == "struct SomeStruct *"); 565 | REQUIRE(nameof::nameof_full_type() == "struct SomeStruct &"); 566 | REQUIRE(nameof::nameof_full_type() == "struct SomeStruct"); 567 | REQUIRE(nameof::nameof_full_type() == "struct SomeStruct *"); 568 | REQUIRE(nameof::nameof_full_type() == "struct SomeStruct &"); 569 | REQUIRE(nameof::nameof_full_type() == "struct SomeStruct const volatile *"); 570 | 571 | REQUIRE(nameof::nameof_full_type>() == "class SomeClass"); 572 | REQUIRE(nameof::nameof_full_type volatile *>() == "class SomeClass const volatile *"); 573 | 574 | REQUIRE(nameof::nameof_full_type() == "struct Long"); 575 | REQUIRE(nameof::nameof_full_type() == "struct Long"); 576 | REQUIRE(nameof::nameof_full_type() == "struct Long::LL"); 577 | 578 | REQUIRE(nameof::nameof_full_type() == "enum Color"); 579 | #elif defined(__GNUC__) 580 | REQUIRE(type_name == "SomeStruct"); 581 | REQUIRE(nameof::nameof_full_type() == "SomeStruct*"); 582 | REQUIRE(nameof::nameof_full_type() == "SomeStruct&"); 583 | REQUIRE(nameof::nameof_full_type() == "SomeStruct"); 584 | REQUIRE(nameof::nameof_full_type() == "SomeStruct*"); 585 | REQUIRE(nameof::nameof_full_type() == "SomeStruct&"); 586 | REQUIRE(nameof::nameof_full_type() == "const volatile SomeStruct*"); 587 | 588 | REQUIRE(nameof::nameof_full_type>() == "SomeClass"); 589 | REQUIRE(nameof::nameof_full_type volatile *>() == "const volatile SomeClass*"); 590 | 591 | REQUIRE(nameof::nameof_full_type() == "Long"); 592 | REQUIRE(nameof::nameof_full_type() == "Long"); 593 | REQUIRE(nameof::nameof_full_type() == "Long::LL"); 594 | 595 | REQUIRE(nameof::nameof_full_type() == "Color"); 596 | #endif 597 | } 598 | 599 | TEST_CASE("nameof::nameof_short_type") { 600 | constexpr auto type_name = nameof::nameof_short_type(); 601 | REQUIRE(type_name == "SomeStruct"); 602 | REQUIRE(nameof::nameof_short_type() == "SomeStruct"); 603 | REQUIRE(nameof::nameof_short_type() == "SomeStruct"); 604 | REQUIRE(nameof::nameof_short_type() == "SomeStruct"); 605 | REQUIRE(nameof::nameof_short_type() == "SomeStruct"); 606 | 607 | REQUIRE(nameof::nameof_short_type>() == "SomeClass"); 608 | REQUIRE(nameof::nameof_short_type volatile>() == "SomeClass"); 609 | 610 | REQUIRE(nameof::nameof_short_type() == "Long"); 611 | REQUIRE(nameof::nameof_short_type() == "Long"); 612 | REQUIRE(nameof::nameof_short_type() == "LL"); 613 | 614 | REQUIRE(nameof::nameof_short_type() == "Color"); 615 | } 616 | 617 | TEST_CASE("NAMEOF_TYPE") { 618 | constexpr auto type_name = NAMEOF_TYPE(decltype(struct_var)); 619 | #if defined(__clang__) 620 | REQUIRE(type_name == "SomeStruct"); 621 | REQUIRE(NAMEOF_TYPE(decltype(ptr_s)) == "SomeStruct *"); 622 | REQUIRE(NAMEOF_TYPE(decltype(ref_s)) == "SomeStruct"); 623 | REQUIRE(NAMEOF_TYPE(SomeStruct) == "SomeStruct"); 624 | REQUIRE(NAMEOF_TYPE(SomeStruct *) == "SomeStruct *"); 625 | REQUIRE(NAMEOF_TYPE(const SomeStruct &) == "SomeStruct"); 626 | REQUIRE(NAMEOF_TYPE(const SomeStruct volatile *) == "const volatile SomeStruct *"); 627 | 628 | REQUIRE(NAMEOF_TYPE(SomeClass) == "SomeClass"); 629 | REQUIRE(NAMEOF_TYPE(const SomeClass volatile *) == "const volatile SomeClass *"); 630 | 631 | REQUIRE(NAMEOF_TYPE(decltype(othervar)) == "Long"); 632 | REQUIRE(NAMEOF_TYPE(Long) == "Long"); 633 | REQUIRE(NAMEOF_TYPE(Long::LL) == "Long::LL"); 634 | 635 | REQUIRE(NAMEOF_TYPE(Color) == "Color"); 636 | #elif defined(_MSC_VER) 637 | REQUIRE(type_name == "struct SomeStruct"); 638 | REQUIRE(NAMEOF_TYPE(decltype(ptr_s)) == "struct SomeStruct *"); 639 | REQUIRE(NAMEOF_TYPE(decltype(ref_s)) == "struct SomeStruct"); 640 | REQUIRE(NAMEOF_TYPE(SomeStruct) == "struct SomeStruct"); 641 | REQUIRE(NAMEOF_TYPE(SomeStruct *) == "struct SomeStruct *"); 642 | REQUIRE(NAMEOF_TYPE(const SomeStruct &) == "struct SomeStruct"); 643 | REQUIRE(NAMEOF_TYPE(const SomeStruct volatile *) == "struct SomeStruct const volatile *"); 644 | 645 | REQUIRE(NAMEOF_TYPE(SomeClass) == "class SomeClass"); 646 | REQUIRE(NAMEOF_TYPE(const SomeClass volatile *) == "class SomeClass const volatile *"); 647 | 648 | REQUIRE(NAMEOF_TYPE(decltype(othervar)) == "struct Long"); 649 | REQUIRE(NAMEOF_TYPE(Long) == "struct Long"); 650 | REQUIRE(NAMEOF_TYPE(Long::LL) == "struct Long::LL"); 651 | 652 | REQUIRE(NAMEOF_TYPE(Color) == "enum Color"); 653 | #elif defined(__GNUC__) 654 | REQUIRE(type_name == "SomeStruct"); 655 | REQUIRE(NAMEOF_TYPE(decltype(ptr_s)) == "SomeStruct*"); 656 | REQUIRE(NAMEOF_TYPE(decltype(ref_s)) == "SomeStruct"); 657 | REQUIRE(NAMEOF_TYPE(SomeStruct) == "SomeStruct"); 658 | REQUIRE(NAMEOF_TYPE(SomeStruct *) == "SomeStruct*"); 659 | REQUIRE(NAMEOF_TYPE(const SomeStruct &) == "SomeStruct"); 660 | REQUIRE(NAMEOF_TYPE(const SomeStruct volatile *) == "const volatile SomeStruct*"); 661 | 662 | REQUIRE(NAMEOF_TYPE(SomeClass) == "SomeClass"); 663 | REQUIRE(NAMEOF_TYPE(const SomeClass volatile *) == "const volatile SomeClass*"); 664 | 665 | REQUIRE(NAMEOF_TYPE(decltype(othervar)) == "Long"); 666 | REQUIRE(NAMEOF_TYPE(Long) == "Long"); 667 | REQUIRE(NAMEOF_TYPE(Long::LL) == "Long::LL"); 668 | 669 | REQUIRE(NAMEOF_TYPE(Color) == "Color"); 670 | #endif 671 | } 672 | 673 | TEST_CASE("NAMEOF_TYPE_EXPR") { 674 | constexpr auto type_name = NAMEOF_TYPE_EXPR(struct_var); 675 | #if defined(__clang__) 676 | REQUIRE(type_name == "SomeStruct"); 677 | REQUIRE(NAMEOF_TYPE_EXPR(ptr_s) == "SomeStruct *"); 678 | REQUIRE(NAMEOF_TYPE_EXPR(ref_s) == "SomeStruct"); 679 | 680 | REQUIRE(NAMEOF_TYPE_EXPR(ptr_c) == "const volatile SomeClass *"); 681 | 682 | REQUIRE(NAMEOF_TYPE_EXPR(othervar) == "Long"); 683 | REQUIRE(NAMEOF_TYPE_EXPR(othervar.ll) == "Long::LL"); 684 | REQUIRE(NAMEOF_TYPE_EXPR(othervar.ll.field) == "int"); 685 | 686 | REQUIRE(NAMEOF_TYPE_EXPR(Color::RED) == "Color"); 687 | 688 | REQUIRE(NAMEOF_TYPE_EXPR(std::declval>()) == "SomeClass"); 689 | #elif defined(_MSC_VER) 690 | REQUIRE(type_name == "struct SomeStruct"); 691 | REQUIRE(NAMEOF_TYPE_EXPR(ptr_s) == "struct SomeStruct *"); 692 | REQUIRE(NAMEOF_TYPE_EXPR(ref_s) == "struct SomeStruct"); 693 | 694 | REQUIRE(NAMEOF_TYPE_EXPR(ptr_c) == "class SomeClass const volatile *"); 695 | 696 | REQUIRE(NAMEOF_TYPE_EXPR(othervar) == "struct Long"); 697 | REQUIRE(NAMEOF_TYPE_EXPR(othervar.ll) == "struct Long::LL"); 698 | REQUIRE(NAMEOF_TYPE_EXPR(othervar.ll.field) == "int"); 699 | 700 | REQUIRE(NAMEOF_TYPE_EXPR(Color::RED) == "enum Color"); 701 | 702 | REQUIRE(NAMEOF_TYPE_EXPR(std::declval>()) == "class SomeClass"); 703 | #elif defined(__GNUC__) 704 | REQUIRE(type_name == "SomeStruct"); 705 | REQUIRE(NAMEOF_TYPE_EXPR(ptr_s) == "SomeStruct*"); 706 | REQUIRE(NAMEOF_TYPE_EXPR(ref_s) == "SomeStruct"); 707 | 708 | REQUIRE(NAMEOF_TYPE_EXPR(ptr_c) == "const volatile SomeClass*"); 709 | 710 | REQUIRE(NAMEOF_TYPE_EXPR(othervar) == "Long"); 711 | REQUIRE(NAMEOF_TYPE_EXPR(othervar.ll) == "Long::LL"); 712 | REQUIRE(NAMEOF_TYPE_EXPR(othervar.ll.field) == "int"); 713 | 714 | REQUIRE(NAMEOF_TYPE_EXPR(Color::RED) == "Color"); 715 | 716 | REQUIRE(NAMEOF_TYPE_EXPR(std::declval>()) == "SomeClass"); 717 | #endif 718 | } 719 | 720 | TEST_CASE("NAMEOF_FULL_TYPE") { 721 | constexpr auto type_name = NAMEOF_FULL_TYPE(decltype(struct_var)); 722 | #if defined(__clang__) 723 | REQUIRE(type_name == "SomeStruct"); 724 | REQUIRE(NAMEOF_FULL_TYPE(decltype(ptr_s)) == "SomeStruct *"); 725 | REQUIRE(NAMEOF_FULL_TYPE(decltype(ref_s)) == "SomeStruct &"); 726 | REQUIRE(NAMEOF_FULL_TYPE(SomeStruct) == "SomeStruct"); 727 | REQUIRE(NAMEOF_FULL_TYPE(SomeStruct *) == "SomeStruct *"); 728 | REQUIRE(NAMEOF_FULL_TYPE(SomeStruct &) == "SomeStruct &"); 729 | REQUIRE(NAMEOF_FULL_TYPE(const SomeStruct volatile *) == "const volatile SomeStruct *"); 730 | 731 | REQUIRE(NAMEOF_FULL_TYPE(SomeClass) == "SomeClass"); 732 | REQUIRE(NAMEOF_FULL_TYPE(const SomeClass volatile *) == "const volatile SomeClass *"); 733 | 734 | REQUIRE(NAMEOF_FULL_TYPE(decltype(othervar)) == "Long"); 735 | REQUIRE(NAMEOF_FULL_TYPE(Long) == "Long"); 736 | REQUIRE(NAMEOF_FULL_TYPE(Long::LL) == "Long::LL"); 737 | 738 | REQUIRE(NAMEOF_FULL_TYPE(Color) == "Color"); 739 | #elif defined(_MSC_VER) 740 | REQUIRE(type_name == "struct SomeStruct"); 741 | REQUIRE(NAMEOF_FULL_TYPE(decltype(ptr_s)) == "struct SomeStruct *"); 742 | REQUIRE(NAMEOF_FULL_TYPE(decltype(ref_s)) == "struct SomeStruct &"); 743 | REQUIRE(NAMEOF_FULL_TYPE(SomeStruct) == "struct SomeStruct"); 744 | REQUIRE(NAMEOF_FULL_TYPE(SomeStruct *) == "struct SomeStruct *"); 745 | REQUIRE(NAMEOF_FULL_TYPE(SomeStruct &) == "struct SomeStruct &"); 746 | REQUIRE(NAMEOF_FULL_TYPE(const SomeStruct volatile *) == "struct SomeStruct const volatile *"); 747 | 748 | REQUIRE(NAMEOF_FULL_TYPE(SomeClass) == "class SomeClass"); 749 | REQUIRE(NAMEOF_FULL_TYPE(const SomeClass volatile *) == "class SomeClass const volatile *"); 750 | 751 | REQUIRE(NAMEOF_FULL_TYPE(decltype(othervar)) == "struct Long"); 752 | REQUIRE(NAMEOF_FULL_TYPE(Long) == "struct Long"); 753 | REQUIRE(NAMEOF_FULL_TYPE(Long::LL) == "struct Long::LL"); 754 | 755 | REQUIRE(NAMEOF_FULL_TYPE(Color) == "enum Color"); 756 | #elif defined(__GNUC__) 757 | REQUIRE(type_name == "SomeStruct"); 758 | REQUIRE(NAMEOF_FULL_TYPE(decltype(ptr_s)) == "SomeStruct*"); 759 | REQUIRE(NAMEOF_FULL_TYPE(decltype(ref_s)) == "SomeStruct&"); 760 | REQUIRE(NAMEOF_FULL_TYPE(SomeStruct) == "SomeStruct"); 761 | REQUIRE(NAMEOF_FULL_TYPE(SomeStruct *) == "SomeStruct*"); 762 | REQUIRE(NAMEOF_FULL_TYPE(SomeStruct &) == "SomeStruct&"); 763 | REQUIRE(NAMEOF_FULL_TYPE(const SomeStruct volatile *) == "const volatile SomeStruct*"); 764 | 765 | REQUIRE(NAMEOF_FULL_TYPE(SomeClass) == "SomeClass"); 766 | REQUIRE(NAMEOF_FULL_TYPE(const SomeClass volatile *) == "const volatile SomeClass*"); 767 | 768 | REQUIRE(NAMEOF_FULL_TYPE(decltype(othervar)) == "Long"); 769 | REQUIRE(NAMEOF_FULL_TYPE(Long) == "Long"); 770 | REQUIRE(NAMEOF_FULL_TYPE(Long::LL) == "Long::LL"); 771 | 772 | REQUIRE(NAMEOF_FULL_TYPE(Color) == "Color"); 773 | #endif 774 | } 775 | 776 | TEST_CASE("NAMEOF_FULL_TYPE_EXPR") { 777 | constexpr auto type_name = NAMEOF_FULL_TYPE_EXPR(struct_var); 778 | #if defined(__clang__) 779 | REQUIRE(type_name == "SomeStruct"); 780 | REQUIRE(NAMEOF_FULL_TYPE_EXPR(ptr_s) == "SomeStruct *"); 781 | REQUIRE(NAMEOF_FULL_TYPE_EXPR(ref_s) == "SomeStruct &"); 782 | 783 | REQUIRE(NAMEOF_FULL_TYPE_EXPR(ptr_c) == "const volatile SomeClass *"); 784 | 785 | REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar) == "Long"); 786 | REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar.ll) == "Long::LL"); 787 | REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar.ll.field) == "int"); 788 | 789 | REQUIRE(NAMEOF_FULL_TYPE_EXPR(Color::RED) == "Color"); 790 | 791 | REQUIRE(NAMEOF_FULL_TYPE_EXPR(std::declval>()) == "const SomeClass &&"); 792 | #elif defined(_MSC_VER) 793 | REQUIRE(type_name == "struct SomeStruct"); 794 | REQUIRE(NAMEOF_FULL_TYPE_EXPR(ptr_s) == "struct SomeStruct *"); 795 | REQUIRE(NAMEOF_FULL_TYPE_EXPR(ref_s) == "struct SomeStruct &"); 796 | 797 | REQUIRE(NAMEOF_FULL_TYPE_EXPR(ptr_c) == "class SomeClass const volatile *"); 798 | 799 | REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar) == "struct Long"); 800 | REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar.ll) == "struct Long::LL"); 801 | REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar.ll.field) == "int"); 802 | 803 | REQUIRE(NAMEOF_FULL_TYPE_EXPR(Color::RED) == "enum Color"); 804 | 805 | REQUIRE(NAMEOF_FULL_TYPE_EXPR(std::declval>()) == "class SomeClass const &&"); 806 | #elif defined(__GNUC__) 807 | REQUIRE(type_name == "SomeStruct"); 808 | REQUIRE(NAMEOF_FULL_TYPE_EXPR(ptr_s) == "SomeStruct*"); 809 | REQUIRE(NAMEOF_FULL_TYPE_EXPR(ref_s) == "SomeStruct&"); 810 | 811 | REQUIRE(NAMEOF_FULL_TYPE_EXPR(ptr_c) == "const volatile SomeClass*"); 812 | 813 | REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar) == "Long"); 814 | REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar.ll) == "Long::LL"); 815 | REQUIRE(NAMEOF_FULL_TYPE_EXPR(othervar.ll.field) == "int"); 816 | 817 | REQUIRE(NAMEOF_FULL_TYPE_EXPR(Color::RED) == "Color"); 818 | 819 | REQUIRE(NAMEOF_FULL_TYPE_EXPR(std::declval>()) == "const SomeClass&&"); 820 | #endif 821 | } 822 | 823 | TEST_CASE("NAMEOF_SHORT_TYPE") { 824 | constexpr auto type_name = NAMEOF_SHORT_TYPE(decltype(struct_var)); 825 | REQUIRE(type_name == "SomeStruct"); 826 | REQUIRE(NAMEOF_SHORT_TYPE(decltype(ref_s)) == "SomeStruct"); 827 | REQUIRE(NAMEOF_SHORT_TYPE(SomeStruct) == "SomeStruct"); 828 | REQUIRE(NAMEOF_SHORT_TYPE(SomeStruct &) == "SomeStruct"); 829 | REQUIRE(NAMEOF_SHORT_TYPE(const SomeStruct volatile) == "SomeStruct"); 830 | 831 | REQUIRE(NAMEOF_SHORT_TYPE(SomeClass) == "SomeClass"); 832 | REQUIRE(NAMEOF_SHORT_TYPE(const SomeClass volatile) == "SomeClass"); 833 | 834 | REQUIRE(NAMEOF_SHORT_TYPE(decltype(othervar)) == "Long"); 835 | REQUIRE(NAMEOF_SHORT_TYPE(Long) == "Long"); 836 | REQUIRE(NAMEOF_SHORT_TYPE(Long::LL) == "LL"); 837 | 838 | REQUIRE(NAMEOF_SHORT_TYPE(Color) == "Color"); 839 | } 840 | 841 | TEST_CASE("NAMEOF_SHORT_TYPE_EXPR") { 842 | constexpr auto type_name = NAMEOF_SHORT_TYPE_EXPR(struct_var); 843 | REQUIRE(type_name == "SomeStruct"); 844 | REQUIRE(NAMEOF_SHORT_TYPE_EXPR(ref_s) == "SomeStruct"); 845 | 846 | REQUIRE(NAMEOF_SHORT_TYPE_EXPR(othervar) == "Long"); 847 | REQUIRE(NAMEOF_SHORT_TYPE_EXPR(othervar.ll) == "LL"); 848 | REQUIRE(NAMEOF_SHORT_TYPE_EXPR(othervar.ll.field) == "int"); 849 | 850 | REQUIRE(NAMEOF_SHORT_TYPE_EXPR(Color::RED) == "Color"); 851 | 852 | REQUIRE(NAMEOF_SHORT_TYPE_EXPR(std::declval>()) == "SomeClass"); 853 | } 854 | 855 | #endif 856 | 857 | #if defined(NAMEOF_TYPE_RTTI_SUPPORTED) && NAMEOF_TYPE_RTTI_SUPPORTED 858 | 859 | TEST_CASE("NAMEOF_TYPE_RTTI") { 860 | TestRtti::Base* ptr = new TestRtti::Derived(); 861 | const TestRtti::Base& const_ref = *ptr; 862 | volatile TestRtti::Base& volatile_ref = *ptr; 863 | volatile const TestRtti::Base& cv_ref = *ptr; 864 | #if defined(__clang__) && !defined(_MSC_VER) 865 | REQUIRE(NAMEOF_TYPE_RTTI(*ptr) == "TestRtti::Derived"); 866 | REQUIRE(NAMEOF_TYPE_RTTI(const_ref) == "TestRtti::Derived"); 867 | REQUIRE(NAMEOF_TYPE_RTTI(volatile_ref) == "TestRtti::Derived"); 868 | REQUIRE(NAMEOF_TYPE_RTTI(cv_ref) == "TestRtti::Derived"); 869 | #elif defined(_MSC_VER) 870 | REQUIRE(NAMEOF_TYPE_RTTI(*ptr) == "struct TestRtti::Derived"); 871 | REQUIRE(NAMEOF_TYPE_RTTI(const_ref) == "struct TestRtti::Derived"); 872 | REQUIRE(NAMEOF_TYPE_RTTI(volatile_ref) == "struct TestRtti::Derived"); 873 | REQUIRE(NAMEOF_TYPE_RTTI(cv_ref) == "struct TestRtti::Derived"); 874 | #elif defined(__GNUC__) 875 | REQUIRE(NAMEOF_TYPE_RTTI(*ptr) == "TestRtti::Derived"); 876 | REQUIRE(NAMEOF_TYPE_RTTI(const_ref) == "TestRtti::Derived"); 877 | REQUIRE(NAMEOF_TYPE_RTTI(volatile_ref) == "TestRtti::Derived"); 878 | REQUIRE(NAMEOF_TYPE_RTTI(cv_ref) == "TestRtti::Derived"); 879 | #endif 880 | } 881 | 882 | TEST_CASE("NAMEOF_FULL_TYPE_RTTI") { 883 | TestRtti::Base* ptr = new TestRtti::Derived(); 884 | const TestRtti::Base& const_ref = *ptr; 885 | volatile TestRtti::Base& volatile_ref = *ptr; 886 | volatile const TestRtti::Base& cv_ref = *ptr; 887 | #if defined(__clang__) && !defined(_MSC_VER) 888 | REQUIRE(NAMEOF_FULL_TYPE_RTTI(*ptr) == "TestRtti::Derived&"); 889 | REQUIRE(NAMEOF_FULL_TYPE_RTTI(const_ref) == "const TestRtti::Derived&"); 890 | REQUIRE(NAMEOF_FULL_TYPE_RTTI(volatile_ref) == "volatile TestRtti::Derived&"); 891 | REQUIRE(NAMEOF_FULL_TYPE_RTTI(cv_ref) == "volatile const TestRtti::Derived&"); 892 | #elif defined(_MSC_VER) 893 | REQUIRE(NAMEOF_FULL_TYPE_RTTI(*ptr) == "struct TestRtti::Derived&"); 894 | REQUIRE(NAMEOF_FULL_TYPE_RTTI(const_ref) == "const struct TestRtti::Derived&"); 895 | REQUIRE(NAMEOF_FULL_TYPE_RTTI(volatile_ref) == "volatile struct TestRtti::Derived&"); 896 | REQUIRE(NAMEOF_FULL_TYPE_RTTI(cv_ref) == "volatile const struct TestRtti::Derived&"); 897 | #elif defined(__GNUC__) 898 | REQUIRE(NAMEOF_FULL_TYPE_RTTI(*ptr) == "TestRtti::Derived&"); 899 | REQUIRE(NAMEOF_FULL_TYPE_RTTI(const_ref) == "const TestRtti::Derived&"); 900 | REQUIRE(NAMEOF_FULL_TYPE_RTTI(volatile_ref) == "volatile TestRtti::Derived&"); 901 | REQUIRE(NAMEOF_FULL_TYPE_RTTI(cv_ref) == "volatile const TestRtti::Derived&"); 902 | #endif 903 | } 904 | 905 | TEST_CASE("NAMEOF_SHORT_TYPE_RTTI") { 906 | TestRtti::Base* ptr = new TestRtti::Derived(); 907 | const TestRtti::Base& const_ref = *ptr; 908 | volatile TestRtti::Base& volatile_ref = *ptr; 909 | volatile const TestRtti::Base& cv_ref = *ptr; 910 | 911 | REQUIRE(NAMEOF_SHORT_TYPE_RTTI(*ptr) == "Derived"); 912 | REQUIRE(NAMEOF_SHORT_TYPE_RTTI(const_ref) == "Derived"); 913 | REQUIRE(NAMEOF_SHORT_TYPE_RTTI(volatile_ref) == "Derived"); 914 | REQUIRE(NAMEOF_SHORT_TYPE_RTTI(cv_ref) == "Derived"); 915 | } 916 | 917 | #endif 918 | 919 | #if defined(NAMEOF_MEMBER_SUPPORTED) && NAMEOF_MEMBER_SUPPORTED 920 | 921 | struct StructMemberInitializationUsingNameof { 922 | std::string teststringfield = std::string{nameof::nameof_member<&StructMemberInitializationUsingNameof::teststringfield>()}; 923 | }; 924 | 925 | struct StructWithNonConstexprDestructor { 926 | ~StructWithNonConstexprDestructor() {} 927 | int somefield; 928 | }; 929 | 930 | TEST_CASE("NAMEOF_MEMBER") { 931 | REQUIRE(NAMEOF_MEMBER(&SomeStruct::somefield) == "somefield"); 932 | REQUIRE(NAMEOF_MEMBER(&SomeStruct::SomeMethod1) == "SomeMethod1"); 933 | REQUIRE(NAMEOF_MEMBER(&Long::LL::field) == "field"); 934 | constexpr auto member_ptr = &SomeStruct::somefield; 935 | REQUIRE(NAMEOF_MEMBER(member_ptr) == "somefield"); 936 | REQUIRE(NAMEOF_MEMBER(&StructMemberInitializationUsingNameof::teststringfield) == "teststringfield"); 937 | REQUIRE(NAMEOF_MEMBER(&StructWithNonConstexprDestructor::somefield) == "somefield"); 938 | } 939 | 940 | TEST_CASE("nameof_member") { 941 | REQUIRE(nameof::nameof_member<&SomeStruct::somefield>() == "somefield"); 942 | REQUIRE(nameof::nameof_member<&SomeStruct::SomeMethod1>() == "SomeMethod1"); 943 | REQUIRE(nameof::nameof_member<&Long::LL::field>() == "field"); 944 | constexpr auto member_ptr = &SomeStruct::somefield; 945 | REQUIRE(nameof::nameof_member() == "somefield"); 946 | REQUIRE(nameof::nameof_member<&StructMemberInitializationUsingNameof::teststringfield>() == "teststringfield"); 947 | REQUIRE(nameof::nameof_member<&StructWithNonConstexprDestructor::somefield>() == "somefield"); 948 | } 949 | 950 | #endif 951 | 952 | #if defined(NAMEOF_POINTER_SUPPORTED) && NAMEOF_POINTER_SUPPORTED 953 | 954 | void somefunction() {} 955 | 956 | TEST_CASE("NAMEOF_POINTER") { 957 | REQUIRE(NAMEOF_POINTER(&SomeStruct::somestaticfield) == "somestaticfield"); 958 | REQUIRE(NAMEOF_POINTER(&SomeStruct::someotherstaticfield) == "someotherstaticfield"); 959 | REQUIRE(NAMEOF_POINTER(static_cast(nullptr)) == "nullptr"); 960 | REQUIRE(NAMEOF_POINTER(static_cast(nullptr)) == "nullptr"); 961 | constexpr auto global_ptr = &someglobalvariable; 962 | REQUIRE(NAMEOF_POINTER(global_ptr) == "someglobalvariable"); 963 | REQUIRE(NAMEOF_POINTER(&someglobalconstvariable) == "someglobalconstvariable"); 964 | REQUIRE(NAMEOF_POINTER(&somefunction) == "somefunction"); 965 | } 966 | 967 | TEST_CASE("nameof_pointer") { 968 | REQUIRE(nameof::nameof_pointer<&SomeStruct::somestaticfield>() == "somestaticfield"); 969 | REQUIRE(nameof::nameof_pointer<&SomeStruct::someotherstaticfield>() == "someotherstaticfield"); 970 | REQUIRE(nameof::nameof_pointer(nullptr)>() == "nullptr"); 971 | REQUIRE(nameof::nameof_pointer(nullptr)>() == "nullptr"); 972 | constexpr auto global_ptr = &someglobalvariable; 973 | REQUIRE(nameof::nameof_pointer() == "someglobalvariable"); 974 | REQUIRE(nameof::nameof_pointer<&someglobalconstvariable>() == "someglobalconstvariable"); 975 | REQUIRE(nameof::nameof_pointer<&somefunction>() == "somefunction"); 976 | } 977 | 978 | #endif 979 | -------------------------------------------------------------------------------- /test/test_aliases.cpp: -------------------------------------------------------------------------------- 1 | // Licensed under the MIT License . 2 | // SPDX-License-Identifier: MIT 3 | // Copyright (c) 2019 - 2024 Daniil Goncharov . 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include 24 | #include 25 | 26 | #define CATCH_CONFIG_MAIN 27 | #include 28 | 29 | struct MyString { 30 | MyString() : str{} {} // required 31 | MyString(const char* s) : str{s} {} // required 32 | MyString(const char* s, std::size_t l) : str{s, l} {} // required 33 | bool empty() const { return str.empty(); } // required 34 | MyString& append(std::size_t count, char c) { str.append(count, c); return *this; } // required 35 | MyString& append(const char* s, std::size_t count) { str.append(s, count); return *this; } // required 36 | MyString& append(const MyString& s) { str.append(s.str); return *this; } // required 37 | 38 | std::size_t size() const { return str.size(); } 39 | int compare(const char* s) const { return str.compare(s); } 40 | 41 | private: 42 | std::string str; 43 | }; 44 | 45 | struct MyStringView { 46 | using value_type = char; // required 47 | static constexpr auto npos = std::string_view::npos; // required 48 | 49 | constexpr MyStringView() : str{} {} // required 50 | constexpr MyStringView(const char* s) : str{s} {} // required 51 | constexpr MyStringView(const char* s, std::size_t size) : str{s, size} {} // required 52 | constexpr bool empty() const { return str.empty(); } // required 53 | constexpr std::size_t size() const { return str.size(); } // required 54 | constexpr const char* data() const { return str.data(); } // required 55 | constexpr const char& operator[](std::size_t i) const { return str[i]; } // required 56 | constexpr void remove_prefix(std::size_t n) { str.remove_prefix(n); } // required 57 | constexpr void remove_suffix(std::size_t n) { str.remove_suffix(n); } // required 58 | constexpr int compare(MyStringView s) const { return str.compare(s.str); } // required 59 | 60 | constexpr int compare(const char* s) const { return str.compare(s); } 61 | 62 | private: 63 | std::string_view str; 64 | 65 | constexpr MyStringView(std::string_view s) : str{s} {} 66 | }; 67 | 68 | #define NAMEOF_USING_ALIAS_STRING using string = MyString; 69 | #define NAMEOF_USING_ALIAS_STRING_VIEW using string_view = MyStringView; 70 | 71 | #include 72 | 73 | enum class Color { RED = 1, GREEN = 2, BLUE = 4 }; 74 | 75 | TEST_CASE("string") { 76 | auto cr = nameof::nameof_enum_flag(Color::RED); 77 | REQUIRE_FALSE(cr.empty()); 78 | REQUIRE(cr.compare("RED") == 0); 79 | 80 | auto crg = nameof::nameof_enum_flag(static_cast(1 | 2)); 81 | REQUIRE_FALSE(crg.empty()); 82 | REQUIRE(crg.compare("RED|GREEN") == 0); 83 | 84 | auto cn = nameof::nameof_enum_flag(Color{0}); 85 | REQUIRE(cn.empty()); 86 | REQUIRE(cn.size() == 0); 87 | } 88 | 89 | TEST_CASE("string_view") { 90 | auto cr = nameof::nameof_enum(Color::RED); 91 | REQUIRE_FALSE(cr.empty()); 92 | REQUIRE(cr.compare("RED") == 0); 93 | 94 | auto cn = nameof::nameof_enum(Color{0}); 95 | REQUIRE(cn.empty()); 96 | REQUIRE(cn.size() == 0); 97 | } 98 | --------------------------------------------------------------------------------