Why Does Typeid.Name() Return Weird Characters Using Gcc and How to Make It Print Unmangled Names

Why does typeid.name() return weird characters using GCC and how to make it print unmangled names?

The return of name is implementation defined : an implementation is not even required to return different strings for different types.

What you get from g++ is a decorated name, that you can "demangle" using the c++filt command or __cxa_demangle.

typeid() returns extra characters in g++

Because it is a pointer to foo. And foo has 3 characters. So it becomes P3foo. The other one has type foo, so it becomes 3foo. Note that the text is implementation dependent, and in this case GCC just gives you the internal, mangled name.

Enter that mangled name into the program c++filt to get the unmangled name:

$ c++filt -t P3foo
foo*

Unmangling the result of std::type_info::name

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;
}

Strange behaviour of the typeid operator?

What's going on is nothing special. Just that typeid doesn't promise to return the "original" name of the type, but just a name.

The function returns an implementation-defined string, which, if you're lucky, is recognizable, but it makes no promise of that.

Getting incorrect class name when using typeid -

There is no requirement on what type_info::name() looks like.

The result of a typeid expression is an lvalue of static type const std::type_info (18.7.1) and dynamic type
const std::type_info or const name where name is an implementation-defined class publicly derived from
std::type_info

Then, about std::type_info::name():

const char* name() const;

Returns: an implementation-defined NTBS.

[...]

NTBS is simply a shorthand for null-terminated byte string.

In other words: You should not rely on any value of type_info::name().

What you actually see with g++:

Those names are mangled names, and g++'s implementation of such mangled names is based on length-prefixed strings, where each substring is the namespace name, plus some other info; but that's basically it.

For example:

unmangled: foo::bar::Frob
mangled: 3foo3bar4Frob

Example to put into your compiler:

#include <iostream>
#include <typeinfo>

namespace foo { namespace bar {
enum Frob {};
class Frobnicate {};
Frob frob;

template <typename T> void Meh() { throw T(); }
} }

int main () {
std::cout << typeid(foo::bar::Frob).name() << '\n'
<< typeid(foo::bar::Frobnicate).name() << '\n'
<< typeid(foo::bar::frob).name() << '\n'
<< typeid(foo::bar::Meh<int>).name() << '\n'
<< typeid(foo::bar::Meh<float>).name() << '\n'
;
}

Output for me:

N3foo3bar4FrobE
N3foo3bar10FrobnicateE
N3foo3bar4FrobE
FvvE
FvvE

The latter two show you that one even cannot rely on names being different.

What does PKc , as the output of typeid(var).name(), mean?

PKc is the mangled name of const char*. P is the encoding for "pointer", K refers to "const", and c means "char".

See also Why does typeid.name() return weird characters using GCC and how to make it print unmangled names?.

How can I get the data type for boost::any variable?

You need to use boost::any's member function type() which returns a const std::type_info& of the contained value

cout << "num1.type().name(): " << num1.type().name() << endl;


Related Topics



Leave a reply



Submit