I posted this answer recently. OP in the StackOverflow question mentioned that it was for educational use, but I can imagine it being used as a debugging aid. Then I decided to improve it slightly and make it a compile-time function. Code:
#include <cassert>
#include <iostream>
#include <string_view>
#include <type_traits>
#include <vector>
#ifndef __GNUC__
static_assert(false, "GCC specific");
#endif // !__GNUC__
// Finds a character in a nul terminated string.
// Returns the last character if sought one isn't found.
// Notes:
// 1. Using this because std::strchr isn't constexpr.
// `PChr` is a separate typename instead of just using Chr* to allow `Chr*` and
// `Chr const*` as the pointer.
// 2. pstr shall not be nullptr.
template <typename PChr, typename Chr>
[[gnu::pure, gnu::nonnull, gnu::returns_nonnull, nodiscard]]
static constexpr auto*
constexpr_strchr(PChr pstr, Chr const value) noexcept {
// PChr must be a raw pointer type because of gnu::nonnull and
// gnu::returns_nonnull
static_assert(std::is_pointer_v<PChr>, "PChr must be a raw pointer type");
auto constexpr nul = Chr{};
while (*pstr != value && *pstr != nul) {
++pstr;
}
return pstr;
}
// Returns distance from ptr to the end of the array.
// Notes:
// 1. ptr shall not be nullptr
// 2. ptr shall be inside the array (arr)
template <typename T, auto size>
[[gnu::const, gnu::artificial, gnu::nonnull, gnu::always_inline, nodiscard]]
inline static constexpr auto
distance_to_end(const T (&arr)[size], T const* const ptr) noexcept {
return arr + size - ptr;
}
// Returns type T as a string_view.
// Ex: std::string -> "std::string"
template <typename T>
[[gnu::const, nodiscard]]
static constexpr auto type_name_finder() noexcept {
// __PRETTY_FUNCTION__ means "$FUNCTION_SIGNATURE [with T = $TYPE]".
// +2 here to skip "= "
auto const* const begin = constexpr_strchr(__PRETTY_FUNCTION__, '=') + 2;
// -2 meaning up to "]0円"
auto const size =
static_cast<std::size_t>(distance_to_end(__PRETTY_FUNCTION__, begin) - 2);
return std::string_view{begin, size};
}
// Inline string_view with the type name.
template <typename T>
inline constexpr auto type_name = type_name_finder<T>();
// Example Class
template <typename T1, typename T2>
class my_class {};
int main() {
// Example use-case
my_class<int&, std::vector<double>> my_arr[20];
std::cout << type_name<decltype(my_arr)>;
}
Code uses __PRETTY_FUNCTION__
in GCC and Clang to get a variable's type as a string. I would appreciate any comments on correctness and readability. I used a lot of GCC specific attributes, because __PRETTY_FUNCTION__
is GCC specific and for this reason the code can't be portable anyway.
2 Answers 2
You're trying to reinvent the wheel... This specific challenge was discussed in answers to this SO question: Read HowardHinnant's long answer, then read the final, pretty, constexpr
answer here. @Snowhawk quoted the code there - now you have some references and the build-up to the final form of the code.
-
\$\begingroup\$ I should have looked around before spending time on this thing. \$\endgroup\$Aykhan Hagverdili– Aykhan Hagverdili2020年03月06日 15:40:22 +00:00Commented Mar 6, 2020 at 15:40
// __PRETTY_FUNCTION__ means "$FUNCTION_SIGNATURE [with T = $TYPE]".
When you call type_name_finder()
, the only part that changes in __PRETTY_FUNCTION__
is $TYPE
. We can use this information to strip the static decoration surrounding that type.
template <typename T>
static constexpr auto type_name_finder() {
std::string_view name, prefix, suffix;
#if defined(__clang__)
name = __PRETTY_FUNCTION__;
prefix = "auto type_name_finder() [T = ";
suffix = "]";
#elif defined(__GNUC__)
name = __PRETTY_FUNCTION__;
prefix = "constexpr auto type_name_finder() [with T = ";
suffix = "]";
#endif
name.remove_prefix(prefix.size());
name.remove_suffix(suffix.size());
return name;
}
-
\$\begingroup\$ MSVC version won't work properly because for built in types
auto __cdecl type_name_finder<class
does not have the "class" word. \$\endgroup\$Aykhan Hagverdili– Aykhan Hagverdili2019年12月29日 08:16:50 +00:00Commented Dec 29, 2019 at 8:16 -
\$\begingroup\$
std::string_view::remove_prefix
is notnoexcept
. I don't think it's very appropriate to marktype_name_finder
not-throwing. \$\endgroup\$Aykhan Hagverdili– Aykhan Hagverdili2019年12月29日 08:17:58 +00:00Commented Dec 29, 2019 at 8:17 -
\$\begingroup\$ Besides that, your implementation is great (+1). Thanks for this much improved version! \$\endgroup\$Aykhan Hagverdili– Aykhan Hagverdili2019年12月29日 08:18:41 +00:00Commented Dec 29, 2019 at 8:18
-
\$\begingroup\$ And what if the actual prefix or suffix isn't the one you expect? To me it looks like undefined behavior then. \$\endgroup\$Roland Illig– Roland Illig2019年12月29日 09:46:29 +00:00Commented Dec 29, 2019 at 9:46