Getting the type of a template argument as string – without RTTI

I recently had the need to retrieve the type of a template argument as a human-readable string for debugging purposes, but without using RTTI – so typeid and type_info were out of the question.

After a bit of trial and error, I came up with the following solution. I thought it was a nice little self-contained piece of code, so I wanted to share it with everybody:

namespace internal
{
  static const unsigned int FRONT_SIZE = sizeof("internal::GetTypeNameHelper<") - 1u;
  static const unsigned int BACK_SIZE = sizeof(">::GetTypeName") - 1u;

  template <typename T>
  struct GetTypeNameHelper
  {
    static const char* GetTypeName(void)
    {
      static const size_t size = sizeof(__FUNCTION__) - FRONT_SIZE - BACK_SIZE;
      static char typeName[size] = {};
      memcpy(typeName, __FUNCTION__ + FRONT_SIZE, size - 1u);

      return typeName;
    }
  };
}


template <typename T>
const char* GetTypeName(void)
{
  return internal::GetTypeNameHelper<T>::GetTypeName();
}

Under MSVC, this nicely yields the following:

struct Foo {};
class Bar {};

GetTypeName<int>()    -> "int"
GetTypeName<float>()  -> "float"
GetTypeName<Foo>()    -> "struct Foo"
GetTypeName<Bar>()    -> "class Bar"

It should be straightforward to understand how the code works. The only thing worth mentioning is that we defer the actual implementation to a static function inside a class template, in order to make the type T become a part of __FUNCTION__. Otherwise, __FUNCTION__ will expand to “GetTypeName”, not including the actual type of the provided T.

The code provided above works with MSVC and Clang. Under GCC, you need to use __PRETTY_FUNCTION__ and adapt FRONT_SIZE and BACK_SIZE accordingly.

Note that, unfortunately, we have to resort to __FUNCTION__ and __PRETTY_FUNCTION__ and cannot use __func__ as described by the C++11 standard, because even __func__ is specified as containing an implementation-defined string. And in the case of the compilers I tested, __func__ evaluates to “GetTypeName”, not including any information about T.

Advertisements

17 thoughts on “Getting the type of a template argument as string – without RTTI

  1. Pingback: 1 โ€“ Getting the type of a template argument as string in C++ โ€“ without RTTI

  2. Nice article, but I really think you didn’t need to use buffers or even `memcpy`. You could’ve just done something like:

    return std::string(__FUNCTION__ + FRONT_SIZE, size – 1u) ;

    • No, using std::string would be much worse in my opinion.
      It needs to allocate memory from the heap internally, and of course also needs to do a copy.

      In MSVC you can also do

      static char typeName[] = __FUNCTION__;

      and put in a null-terminator yourself. But that only works in MSVC, and not when __FUNCTION__ or __PRETTY_FUNCTION__ is of type static const char[].

      • No because:

        – Modern string implementations use the short-string optimization, so no memory is allocated.
        – GCC 5 actually inlines the entire constructor body.
        – It’s for debugging purposes anyway…

        Ultimately, it’s just the case of avoiding writing C-style code in modern C++. Since you already have `std::string`, you should use it. See the C++ Core Guidelines ES.2: https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#-es2-prefer-suitable-abstractions-to-direct-use-of-language-features

      • Strongly disagree with the suggestion to use std::string just because it’s there.
        I don’t use std::string anywhere in the engine runtime, and I’m not going to pull in a dependency to std::string for a debugging feature, when a buffer & memcpy gets the job done.

      • > Modern string implementations use the short-string optimization, so no memory is allocated.

        How can you guarantee that the short-string optimization in a particular compiler 1) exists 2) has big enough buffer to fit all possible type names?

      • Well, you never said you didn’t use `std::string` anywhere else, so I understand it a bit better now. ๐Ÿ˜‰

        I personally use it when I can, then only replace it with C-style strings if I know that it will be significantly faster.

      • Well, you never said you didnโ€™t use `std::string` anywhere else, so I understand it a bit better now. ๐Ÿ˜‰

        True :).
        I use neither std::string nor any C-style replacement in the runtime part of the engine, only hashed strings for identifying resources. The only exception are UTF-8 strings used for localizable text being displayed on the screen.

  3. This uses macros and requires that you “define each type”, but it’s platform/compiler agnostic.
    http://cpp.sh/2tezu
    #include

    template
    const char *GetTypeName();

    #define MAKE_TEMPLATE_NAME(T) \
    template \
    const char *GetTypeName() { return #T; }

    struct Foo {};
    class Bar {};

    MAKE_TEMPLATE_NAME(int);
    MAKE_TEMPLATE_NAME(float);
    MAKE_TEMPLATE_NAME(Foo);
    MAKE_TEMPLATE_NAME(Bar);

    int main()
    {
    std::cout << GetTypeName() << std::endl;
    std::cout << GetTypeName() << std::endl;
    std::cout << GetTypeName() << std::endl;
    std::cout << GetTypeName() << std::endl;
    }

  4. We wrote a library on compile-time type information (CTTI) based on this __PRETTY_FUNCTION__ trick some months ago https://github.com/Manu343726/ctti. The idea is to store and hash type names at compile time thanks to constexpr support. It works in GCC, Clang, and VS2015.
    However it has the caveat that, since we want the string type to be unique (Not dependent on string length), the string storage is fixed (You can set it currently via a CTTI_MAX_STRING_LENGTH macro). We have an old and looong issue regarding different approaches for string storage https://github.com/Manu343726/ctti/issues/5. I will be glad if you consider our project interesting enough to share your thoughts there.

  5. Also let me write again just to say this is one of the best C++ blogs out there. I have the job-stealing task engine in my TODO list since the very first time I checked out this blog. Amazing work.

  6. I’ve seen this trick fail on some compiler flavors, so don’t assume full portability. In particular, a certain game console (nda) has a flavor of clang that erroneously reports “value_type” in the function macro, regardless of the type in the actual instantiation.

  7. Hello, with a constexpr (C++14) evaluation it would work properly ; e.g emitting at compile time ; yes for the std::string suggestion, using non-POD types is not the thing to do or even think about, was weird.

  8. Hi Stefan,

    Thank you for sharing this very cool trick! I actually integrated it into one of my projects as an alternative to using C++ RTTI (not just for debugging). You can see the relevant commit and specific section of code I modified (with credit to you) here:

    https://github.com/amaiorano/hsm/commit/f5c3b9a5c23fd07a2092336b1bc6d6f82526a022#diff-02ce6e99bf2b3da47e4d0d45b5436caaR170

    I wanted to ask you about licensing. The code that you share on your blog, is it under some sort of open source license (MIT?). Just want to make sure it’s okay for me to use this code.

    Thanks, and keep up the great blog!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s