4
\$\begingroup\$

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.

asked Dec 28, 2019 at 13:17
\$\endgroup\$
0

2 Answers 2

2
\$\begingroup\$

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.

answered Feb 13, 2020 at 9:36
\$\endgroup\$
1
  • \$\begingroup\$ I should have looked around before spending time on this thing. \$\endgroup\$ Commented Mar 6, 2020 at 15:40
4
\$\begingroup\$
 // __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;
}
answered Dec 29, 2019 at 7:59
\$\endgroup\$
4
  • \$\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\$ Commented Dec 29, 2019 at 8:16
  • \$\begingroup\$ std::string_view::remove_prefix is not noexcept. I don't think it's very appropriate to mark type_name_finder not-throwing. \$\endgroup\$ Commented Dec 29, 2019 at 8:17
  • \$\begingroup\$ Besides that, your implementation is great (+1). Thanks for this much improved version! \$\endgroup\$ Commented 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\$ Commented Dec 29, 2019 at 9:46

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.