This trick was mentioned under a few other questions, but not here yet.
All major compilers support __PRETTY_FUNC__
(GCC & Clang) /__FUNCSIG__
(MSVC) as an extension.
When used in a template like this:
template <typename T> const char *foo(){ #ifdef _MSC_VER return __FUNCSIG__; #else return __PRETTY_FUNCTION__; #endif}
It produces strings in a compiler-dependent format, that contain, among other things, the name of T
.
E.g. foo<float>()
returns:
"const char* foo() [with T = float]"
on GCC"const char *foo() [T = float]"
on Clang"const char *__cdecl foo<float>(void)"
on MSVC
You can easily parse the type names out of those strings. You just need to figure out how many 'junk' characters your compiler inserts before and after the type.
You can even do that completely at compile-time.
The resulting names can slightly vary between different compilers. E.g. Clang omits default template arguments, and MSVC adds prefixes class
/struct
/union
/enum
to the respective types.
Here's an implementation that I've been using. Everything is done at compile-time.
Example usage:
std::cout << TypeName<float>() << '\n';std::cout << TypeName<decltype(1.2f)>(); << '\n';
Implementation: (uses C++20, but can be backported; see the edit history for a C++17 version)
#include <algorithm>#include <array>#include <cstddef>#include <string_view>namespace impl{ template <typename T> [[nodiscard]] constexpr std::string_view RawTypeName() { #ifndef _MSC_VER return __PRETTY_FUNCTION__; #else return __FUNCSIG__; #endif } struct TypeNameFormat { std::size_t junk_leading = 0; std::size_t junk_total = 0; }; constexpr TypeNameFormat type_name_format = []{ TypeNameFormat ret; std::string_view sample = RawTypeName<int>(); ret.junk_leading = sample.find("int"); ret.junk_total = sample.size() - 3; return ret; }(); static_assert(type_name_format.junk_leading != std::size_t(-1), "Unable to determine the type name format on this compiler."); template <typename T> static constexpr auto type_name_storage = []{ std::array<char, RawTypeName<T>().size() - type_name_format.junk_total + 1> ret{}; std::copy_n(RawTypeName<T>().data() + type_name_format.junk_leading, ret.size() - 1, ret.data()); return ret; }();}template <typename T>[[nodiscard]] constexpr std::string_view TypeName(){ return {impl::type_name_storage<T>.data(), impl::type_name_storage<T>.size() - 1};}template <typename T>[[nodiscard]] constexpr const char *TypeNameCstr(){ return impl::type_name_storage<T>.data();}