127

I'm currently working on some logging code that supposed to - among other things - print information about the calling function. This should be relatively easy, standard C++ has a type_info class. This contains the name of the typeid'd class/function/etc. but it's mangled. It's not very useful. I.e. typeid(std::vector<int>).name() returns St6vectorIiSaIiEE.

Is there a way to produce something useful from this? Like std::vector<int> for the above example. If it only works for non-template classes, that's fine too.

The solution should work for gcc, but it would be better if I could port it. It's for logging so it's not so important that it can't be turned off, but it should be helpful for debugging.

Jeffrey Bosboom
  • 13,313
  • 16
  • 79
  • 92
terminus
  • 13,745
  • 8
  • 34
  • 37

15 Answers15

143

Given the attention this question / answer receives, and the valuable feedback from GManNickG, I have cleaned up the code a little bit. Two versions are given: one with C++11 features and another one with only C++98 features.

In file type.hpp

#ifndef TYPE_HPP
#define TYPE_HPP

#include <string>
#include <typeinfo>

std::string demangle(const char* name);

template <class T>
std::string type(const T& t) {

    return demangle(typeid(t).name());
}

#endif

In file type.cpp (requires C++11)

#include "type.hpp"
#ifdef __GNUG__
#include <cstdlib>
#include <memory>
#include <cxxabi.h>

std::string demangle(const char* name) {

    int status = -4; // some arbitrary value to eliminate the compiler warning

    // enable c++11 by passing the flag -std=c++11 to g++
    std::unique_ptr<char, void(*)(void*)> res {
        abi::__cxa_demangle(name, NULL, NULL, &status),
        std::free
    };

    return (status==0) ? res.get() : name ;
}

#else

// does nothing if not g++
std::string demangle(const char* name) {
    return name;
}

#endif

Usage:

#include <iostream>
#include "type.hpp"

struct Base { virtual ~Base() {} };

struct Derived : public Base { };

int main() {

    Base* ptr_base = new Derived(); // Please use smart pointers in YOUR code!

    std::cout << "Type of ptr_base: " << type(ptr_base) << std::endl;

    std::cout << "Type of pointee: " << type(*ptr_base) << std::endl;

    delete ptr_base;
}

It prints:

Type of ptr_base: Base*
Type of pointee: Derived

Tested with g++ 4.7.2, g++ 4.9.0 20140302 (experimental), clang++ 3.4 (trunk 184647), clang 3.5 (trunk 202594) on Linux 64 bit and g++ 4.7.2 (Mingw32, Win32 XP SP2).

If you cannot use C++11 features, here is how it can be done in C++98, the file type.cpp is now:

#include "type.hpp"
#ifdef __GNUG__
#include <cstdlib>
#include <memory>
#include <cxxabi.h>

struct handle {
    char* p;
    handle(char* ptr) : p(ptr) { }
    ~handle() { std::free(p); }
};

std::string demangle(const char* name) {

    int status = -4; // some arbitrary value to eliminate the compiler warning

    handle result( abi::__cxa_demangle(name, NULL, NULL, &status) );

    return (status==0) ? result.p : name ;
}

#else

// does nothing if not g++
std::string demangle(const char* name) {
    return name;
}

#endif


(Update from Sep 8, 2013)

The accepted answer (as of Sep 7, 2013), when the call to abi::__cxa_demangle() is successful, returns a pointer to a local, stack allocated array... ouch!
Also note that if you provide a buffer, abi::__cxa_demangle() assumes it to be allocated on the heap. Allocating the buffer on the stack is a bug (from the gnu doc): "If output_buffer is not long enough, it is expanded using realloc." Calling realloc() on a pointer to the stack... ouch! (See also Igor Skochinsky's kind comment.)

You can easily verify both of these bugs: just reduce the buffer size in the accepted answer (as of Sep 7, 2013) from 1024 to something smaller, for example 16, and give it something with a name not longer than 15 (so realloc() is not called). Still, depending on your system and the compiler optimizations, the output will be: garbage / nothing / program crash.
To verify the second bug: set the buffer size to 1 and call it with something whose name is longer than 1 character. When you run it, the program almost assuredly crashes as it attempts to call realloc() with a pointer to the stack.


(The old answer from Dec 27, 2010)

Important changes made to KeithB's code: the buffer has to be either allocated by malloc or specified as NULL. Do NOT allocate it on the stack.

It's wise to check that status as well.

I failed to find HAVE_CXA_DEMANGLE. I check __GNUG__ although that does not guarantee that the code will even compile. Anyone has a better idea?

#include <cxxabi.h>

const string demangle(const char* name) {

    int status = -4;

    char* res = abi::__cxa_demangle(name, NULL, NULL, &status);

    const char* const demangled_name = (status==0)?res:name;

    string ret_val(demangled_name);

    free(res);

    return ret_val;
}
Community
  • 1
  • 1
Ali
  • 56,466
  • 29
  • 168
  • 265
  • Can you explain why the buffer cannot be allocated on the stack? Because so far it worked fine for me. – terminus May 23 '12 at 18:56
  • Last time I checked `abi::__cxa_demangle` expected it to be allocated on the stack. I guess it calls `realloc` if the buffer is not large enough. So if your buffer on the stack happens to be sufficiently large then it won't call realloc and this bug remains unnoticed. It is still a bug. – Ali May 24 '12 at 10:42
  • 2
    From the [docs](http://idlebox.net/2008/0901-stacktrace-demangled/cxa_demangle.htt): `output_buffer` A region of memory, allocated with malloc, of *length bytes, into which the demangled name is stored. If output_buffer is not long enough, it is expanded using realloc. output_buffer may instead be NULL; in that case, the demangled name is placed in a region of memory allocated with malloc. – Igor Skochinsky Aug 29 '12 at 18:31
  • 2
    @IgorSkochinsky Yes, there is a typo in my previous comment but I cannot edit that. What I wanted to write: "Last time I checked `abi::__cxa_demangle` expected it to be allocated **on the heap.**" Thank you very much for looking up the doc! – Ali Aug 29 '12 at 19:05
  • 1
    Note that technically this can leak if `ret_val` throws during construction. You can use a scope guard to guard against that. – GManNickG Aug 11 '13 at 19:18
  • @GManNickG Would you be so kind to check my updated answer? I hope the code is now bug free. Many thanks! – Ali Sep 08 '13 at 13:25
  • @terminus I am glad you find it helpful. :) – Ali Sep 10 '13 at 21:28
  • 3
    If would probably be more clear to use `std::unique_ptr` as the signature for your pointer. – mindvirus Aug 25 '14 at 17:53
  • @mindvirus Yes, thanks, I saw your earlier edit request. However, I am not in favor of this change: I have had a very weird compile error when passing `&tolower` (`::tolower` vs. `std::tolower`) in a similar situation. This was most likely a compiler quirk but unless you can *guarantee that all compilers* handle `decltype(&std::free)` properly in all cases, I leave it the way it is; the version in the answer will work. – Ali Aug 25 '14 at 18:43
  • Even incorrect compilers :)? Anyway, this was the pattern that cplusplus.com documents suggest: http://www.cplusplus.com/reference/memory/unique_ptr/~unique_ptr/ – mindvirus Aug 25 '14 at 19:14
  • @mindvirus Don't get me wrong: In theory, it should be done the way you suggest, you are absolutely right. Unfortunately, in practice, many people are stuck with an older (quirky) compiler. Nobody has ever complained about the current solution not compiling; and at the moment I don't have time to verify whether your suggested change works correctly on older versions of gcc / clang (yes, even if they are somewhat broken). Sorry... – Ali Aug 25 '14 at 21:22
  • Heh, no worries, just saw it passing by, turned out it was a waaay deeper rabbit hole than I expected. – mindvirus Aug 25 '14 at 21:24
  • Wait, isn't the memory malloc'ed by __cxa_demangle going to get free'ed as it is returned as res.get()? That's not going to work when someone tries to use that returned pointer since it's already been free'ed. You'd need to strdup the res.get() and the caller of the function will have to call free themselves no? EDIT: nvm, I didn't see it was being returned as a std::string. – wjwwood Jul 01 '15 at 04:07
  • @Ali I tested the last snippet in your post and works fine. Just a point about it. Since some people doesn't include the namespace std, to avoid compilation errors it would be nice if you could replace `string` with `std::string`. Thank-you – user9869932 Oct 06 '15 at 23:29
  • @julianromera Thanks for the feedback. However, I don't see any unqualified `string` in the *proposed solution*: It uses `std::string` everywhere. I only see a single `string` in the original post from 2010, which is certainly not the solution that I would recommend. Where do you see `string` in the proposed solution? – Ali Oct 07 '15 at 00:19
  • @Ali Oh I must have missed that, now I see that you are right. – user9869932 Oct 07 '15 at 01:10
  • ``HAVE_CXA_DEMANGLE`` is something your configure scripts would test for and then set either on the compiler command line or in a ``config.h`` file. – jcoffland Oct 14 '20 at 23:34
38

Boost core contains a demangler. Checkout core/demangle.hpp:

#include <boost/core/demangle.hpp>
#include <typeinfo>
#include <iostream>

template<class T> struct X
{
};

int main()
{
    char const * name = typeid( X<int> ).name();

    std::cout << name << std::endl; // prints 1XIiE
    std::cout << boost::core::demangle( name ) << std::endl; // prints X<int>
}

It's basically just a wrapper for abi::__cxa_demangle, as has been suggested previously.

moof2k
  • 1,678
  • 1
  • 17
  • 19
18

If all we want is the unmangled type name for the purpose of logging, we can actually do this without using std::type_info or even RTTI at all.

A slightly portable solution that works for the big 3 main compiler front-ends (, , and ) would be to use a function template and extract the type name from the function name.

gcc and clang both offer __PRETTY_FUNCTION__ which is the name of a current function or function template with all type-argument in the string. Similarly MSVC has __FUNCSIG__ which is equivalent. Each of these are formatted a little differently, for example, for a call of void foo<int>, the compilers will output something different:

  • gcc is formatted void foo() [with T = int; ]
  • clang is formatted void foo() [T = int]
  • msvc is formatted void foo<int>()

Knowing this, it's just a matter of parsing out a prefix and suffix and wrapping this into a function in order to extract out the type name.

We can even use 's std::string_view and extended constexpr to get string names at compile-time, just by parsing the name of a template function. This could also be done in any earlier C++ version, but this will still require some form of string parsing.

For example:

#include <string_view>

template <typename T>
constexpr auto get_type_name() -> std::string_view
{
#if defined(__clang__)
    constexpr auto prefix = std::string_view{"[T = "};
    constexpr auto suffix = "]";
    constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
#elif defined(__GNUC__)
    constexpr auto prefix = std::string_view{"with T = "};
    constexpr auto suffix = "; ";
    constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
#elif defined(_MSC_VER)
    constexpr auto prefix = std::string_view{"get_type_name<"};
    constexpr auto suffix = ">(void)";
    constexpr auto function = std::string_view{__FUNCSIG__};
#else
# error Unsupported compiler
#endif

    const auto start = function.find(prefix) + prefix.size();
    const auto end = function.find(suffix);
    const auto size = end - start;

    return function.substr(start, size);
}

With this, you can call get_type_name<T>() to get a std::string_view at compile-time indicating the unmangled type name.

For example:

std::cout << get_type_name<std::string>() << std::endl;

on GCC will output:

std::__cxx11::basic_string<char>

and on clang will output:

std::basic_string<char>

Live Example


A similar augmentation to this approach which avoids a prefix and suffix is to assume that the function name is the same for all types, and search for a sentinel type to parse out the offset to the sentinel from each end. This ensures that the string searching only happens once, and the offset is assumed to find the string name each time. For example, using double as a simple sentinel:

template <typename T>
constexpr auto full_function_name() -> std::string_view
{
#if defined(__clang__) || defined(__GNUC__)
    return std::string_view{__PRETTY_FUNCTION__};
#elif defined(_MSC_VER)
    return std::string_view{__FUNCSIG__};
#else
# error Unsupported compiler
#endif
}

// Outside of the template so its computed once
struct type_name_info {
    static constexpr auto sentinel_function = full_function_name<double>();
    static constexpr auto prefix_offset = sentinel_function.find("double");
    static constexpr auto suffix_offset = sentinel_function.size() - prefix_offset - /* strlen("double") */ 6;
};

template <typename T>
constexpr auto get_type_name() -> std::string_view
{
    constexpr auto function = full_function_name<T>();

    const auto start = type_name_info::prefix_offset;
    const auto end = function.size() - type_name_info::suffix_offset;
    const auto size = end - start;

    return function.substr(start, size);
}

Live Example


This isn't portable to all compilers, but can be modified for any compiler that offers a __FUNCSIG__/__PRETTY_FUNCTION__ equivalent; it just requires a bit of parsing.

note: This hasn't been fully tested, so there may be some bugs; but the primary idea is to parse any output that contains the name in totality -- which is often a side-effect of __func__-like outputs on compilers.

Armut
  • 969
  • 8
  • 22
Human-Compiler
  • 11,022
  • 1
  • 32
  • 59
11

This is what we use. HAVE_CXA_DEMANGLE is only set if available (recent versions of GCC only).

#ifdef HAVE_CXA_DEMANGLE
const char* demangle(const char* name)
{
   char buf[1024];
    unsigned int size=1024;
    int status;
    char* res = abi::__cxa_demangle (name,
                                 buf,
                                 &size,
                                 &status);
    return res;
  }
#else
const char* demangle(const char* name)
{
  return name;
}
#endif  
KeithB
  • 16,577
  • 3
  • 41
  • 45
11

Here, take a look at type_strings.hpp it contains a function that does what you want.

If you just look for a demangling tool, which you e.g. could use to mangle stuff shown in a log file, take a look at c++filt, which comes with binutils. It can demangle C++ and Java symbol names.

Wolf
  • 9,679
  • 7
  • 62
  • 108
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • 1
    Just to note, both cxa_demange() (which the code linked to uses) and cx++filt are gcc specific. There is no portable way to do this. – KeithB Nov 11 '08 at 19:57
  • c++filt doesn't cut it, I need this stuff (or most of it) at compile-time, mostly done with macros. – terminus Nov 11 '08 at 20:11
  • 4
    Link to type_strings.cpp seems broken. – StackedCrooked May 02 '11 at 11:14
  • 1
    Hi @GregoryPakosz The github link in your above comment seems broken too :( Cheers – oHo Mar 06 '15 at 22:58
  • Just an important FYI: `abi::__cxa_demangle()` and its ilk from `` _**are not GCC-specific**_ – they may have been GCC-only in the distant past, but by this post’s time of writing, `` was an entrenchedly ad-hoc standard. So while the answer’s code link was DOI, I can vouch for Clang providing first-class support in this case… q.v., from the Clang’s `libcxxabi` source: the respective decl, impl, huge test: http://git.io/vRTBo, http://git.io/vRTBh, http://git.io/vRTRf – the test code’s comments note the Clang implementation as capable of *more* demangling, somehow, vs. GCC. – fish2000 Dec 02 '15 at 12:01
5

It's implementation defined, so it's not something that's going to be portable. In MSVC++, name() is the undecorated name, and you have to look at raw_name() to get the decorated one.
Just a stab in the dark here, but under gcc, you might want to look at demangle.h

Eclipse
  • 44,851
  • 20
  • 112
  • 171
4

I also found a macro called __PRETTY_FUNCTION__, which does the trick. It gives a pretty function name (figures :)). This is what I needed.

I.e. it gives me the following:

virtual bool mutex::do_unlock()

But I don't think it works on other compilers.

terminus
  • 13,745
  • 8
  • 34
  • 37
3

The accepted solution [1] works mostly well. I found at least one case (and I wouldn't call it a corner case) where it does not report what I expected... with references.

For those cases, I found another solution, posted at the bottom.

Problematic case (using type as defined in [1]):

int i = 1;
cout << "Type of " << "i" << " is " << type(i) << endl;
int & ri = i;
cout << "Type of " << "ri" << " is " << type(ri) << endl;

produces

Type of i is int
Type of ri is int

Solution (using type_name<decltype(obj)>(), see code below):

cout << "Type of " << "i" << " is " << type_name<decltype(i)>() << endl;
cout << "Type of " << "ri" << " is " << type_name<decltype(ri)>() << endl;

produces

Type of i is int
Type of ri is int&

as desired (at least by me)

Code . It has to be in an included header, not in a separately compiled source, due to specialization issues. See undefined reference to template function for instance.

#ifndef _MSC_VER
#   include <cxxabi.h>
#endif
#include <memory>
#include <string>
#include <cstdlib>

template <class T>
std::string
type_name()
{
    typedef typename std::remove_reference<T>::type TR;
    std::unique_ptr<char, void(*)(void*)> own
           (
#ifndef _MSC_VER
                abi::__cxa_demangle(typeid(TR).name(), nullptr,
                                           nullptr, nullptr),
#else
                nullptr,
#endif
                std::free
           );
    std::string r = own != nullptr ? own.get() : typeid(TR).name();
    if (std::is_const<TR>::value)
        r += " const";
    if (std::is_volatile<TR>::value)
        r += " volatile";
    if (std::is_lvalue_reference<T>::value)
        r += "&";
    else if (std::is_rvalue_reference<T>::value)
        r += "&&";
    return r;
}
2

Not a complete solution, but you may want to look at what some of the standard (or widely supported) macro's define. It's common in logging code to see the use of the macros:

__FUNCTION__
__FILE__
__LINE__

e.g.:

log(__FILE__, __LINE__, __FUNCTION__, mymessage);
luke
  • 36,103
  • 8
  • 58
  • 81
  • 4
    Not to mention __PRETTY_FUNCTION__. – CesarB Nov 11 '08 at 19:26
  • 2
    This will give you the information about where you are in the code. What the question was asking was a pretty name of a type, like std::vector. – KeithB Nov 11 '08 at 19:56
  • He mentioned it was for debugging, and I stated it wasn't a complete solution. Other macros such as __FUNCDNAME__ will return the decorated name. – luke Nov 11 '08 at 20:07
  • Actually, re-reading the question, it was "I'm currently working on some logging code that supposed to - among other things - print information about the calling function." This works. – Max Lybbert Nov 11 '08 at 22:36
  • It's not complete, since I don't know the namespace. This is allready in my code. But thanks anyway. – terminus Nov 12 '08 at 16:08
2

A slight variation on Ali's solution. If you want the code to still be very similar to

typeid(bla).name(),

writing this instead

Typeid(bla).name() (differing only in capital first letter)

then you may be interested in this:

In file type.hpp

#ifndef TYPE_HPP
#define TYPE_HPP

#include <string>
#include <typeinfo>

std::string demangle(const char* name);

/*
template <class T>
std::string type(const T& t) {

  return demangle(typeid(t).name());
}
*/

class Typeid {
 public:

  template <class T>
    Typeid(const T& t) : typ(typeid(t)) {}

  std::string name() { return demangle(typ.name()); }

 private:
  const std::type_info& typ;
};


#endif

type.cpp stays same as in Ali's solution

matzzz
  • 21
  • 1
2

Following Ali's solution, here is the C++11 templated alternative which worked best for my usage.

// type.h
#include <cstdlib>
#include <memory>
#include <cxxabi.h>

template <typename T>
std::string demangle() {
  int status = -4;

  std::unique_ptr<char, void (*)(void*)> res{
      abi::__cxa_demangle(typeid(T).name(), NULL, NULL, &status), std::free};
  return (status == 0) ? res.get() : typeid(T).name();
}

Usage:

// main.cpp
#include <iostream>

namespace test {
    struct SomeStruct {};
}

int main()
{
    std::cout << demangle<double>() << std::endl;
    std::cout << demangle<const int&>() << std::endl;
    std::cout << demangle<test::SomeStruct>() << std::endl;

    return 0;
}

Will print:

double                                                                        
int                                                                           
test::SomeStruct
Alexis Paques
  • 1,885
  • 15
  • 29
1
// KeithB's solution is good, but has one serious flaw in that unless buf is static
// it'll get trashed from the stack before it is returned in res - and will point who-knows-where
// Here's that problem fixed, but the code is still non-re-entrant and not thread-safe.
// Anyone care to improve it?

#include <cxxabi.h>

// todo: javadoc this properly
const char* demangle(const char* name)
{
    static char buf[1024];
    size_t size = sizeof(buf);
    int status;
    // todo:
    char* res = abi::__cxa_demangle (name,
                                 buf,
                                 &size,
                                 &status);
    buf[sizeof(buf) - 1] = 0; // I'd hope __cxa_demangle does this when the name is huge, but just in case.
    return res;
  }
Dan Dare
  • 47
  • 6
  • 12
    WARNING! The buffer has to be either allocated by malloc or specified as NULL. Do NOT allocate it on the stack. See my code below. – Ali Dec 27 '10 at 20:06
1

Take a look at __cxa_demangle which you can find at cxxabi.h.

CesarB
  • 43,947
  • 7
  • 63
  • 86
  • I took, it's deprecated, according to the message I get. – terminus Nov 11 '08 at 20:10
  • Where did you find that message? I just googled it and it seems to be supported, no evidence of being deprecated. – Ali Dec 27 '10 at 20:22
  • Maybe it's deprecated in :: namespace. Use abi::__cxa_demangle and you won't get a warning. What gcc are you using? – onitake Jul 29 '11 at 01:03
0

I've always wanted to use type_info, but I'm sure that the result of the name() member function is non-standard and won't necessarily return anything that can be converted to a meaningful result.
If you are sticking to one compiler, there maybe a compiler specific function that will do what you want. Check the documentation.

quamrana
  • 37,849
  • 12
  • 53
  • 71
0

boost::typeindex provides something helpful.

#include <boost/type_index.hpp>
#include <iostream>
#include <vector>

class Widget {};

int main() {
  using boost::typeindex::type_id_with_cvr;
  const std::vector<Widget> vw;
  std::cout << type_id_with_cvr<decltype(vw)>().pretty_name() << std::endl;
  std::cout << type_id_with_cvr<decltype(vw[0])>().pretty_name() << std::endl;
  return 0;
}

The output is

std::vector<Widget, std::allocator<Widget> > const
Widget const&

What is worthy of notice is that type_id_with_cvr preserves reference and c/v qualifiers, while typeid doesn't. See the following example:

#include <iostream>
#include <boost/type_index.hpp>
#include <typeindex>
#include <vector>
#include <typeinfo>

class Widget {};

template <typename T>
void f(const T &param) {
  std::cout << typeid(param).name() << std::endl;
  std::cout
      << boost::typeindex::type_id_with_cvr<decltype(param)>().pretty_name()
      << std::endl;
}

int main() {
  const std::vector<Widget> vw(1);
  f(&vw[0]);
  return 0;
}

The output is

PK6Widget
Widget const* const&

Here, typeid produces PK6Widget, which means Pointer to Konst Widget. The number '6' is the length of the name 'Widget'. This is not the correct type of param, in which the reference and const qualifier are dropped.

The type_id_with_cvr actually uses the demangling functions in boost::core, as has been mentioned in this answer. To preserve the cv qualifiers or reference, it just defines an empty template named cvr_saver and then passes cvr_saver<type> to typeid.

Effective Modern C++ Item 4 talks about this.

GKxx
  • 93
  • 7