I was creating a template class that transforms a type into a string describing it, eg typeinfo<int(*)()>::name()
returns the string "int(*)()"
(up to whitespace). Initially I had tons of special cases to work around the fact that typeid(...).name()
strips off reference qualifiers and top-level cv-qualifiers, but then I remembered that passing a type as a template parameter will preserve these. So, using the ABI header, I ended up with something like this:
#include <iostream>
#include <typeinfo>
#include <string>
#include <cxxabi.h>
using namespace std;
string demangle(const char* mangledName) {
int status;
char* result = abi::__cxa_demangle(mangledName, nullptr, nullptr, &status);
switch(status) {
case -1:
cerr << "Out of memory!" << endl;
exit(1);
case -2:
return mangledName;
case -3: // Should never happen, but just in case?
return mangledName;
}
string name = result;
free(result);
return name;
}
template<typename T> struct preserve_qualifiers {};
template<typename T> class typeinfo {
using wrap = preserve_qualifiers<T>;
public:
static const string name() {
string name = demangle(typeid(wrap).name());
int i = name.find_first_of('<');
if(i == string::npos) return name;
int j = name.length() - i - 2;
return name.substr(i + 1, j);
}
};
#define TypeOut(...) cout \
<< "Type " #__VA_ARGS__ ": " << endl \
<< " Mangled: " << typeid(__VA_ARGS__).name() << endl \
<< " Demangled: " << demangle(typeid(__VA_ARGS__).name()) << endl \
<< " typeinfo<>: " << typeinfo<__VA_ARGS__>::name() << endl
class A {};
template<typename T> class F {};
template<int T> class G {};
template<template<typename> class T> class H {};
template<template<int> class T> class I {};
template<typename... T> class J {};
template<int... T> class K {};
template<template<typename> class... T> class L {};
template<template<int> class... T> class M {};
template<template<typename> class... T> class N {};
template<template<template<typename> class...> class... T> class O {};
struct bits {int i : 4, j : 2;};
template<typename T, int n> struct bits2 {T val : n;};
int main(int argc, char* argv[]) {
TypeOut(void(*volatile)(void(*const)()));
TypeOut(int (A::*)());
TypeOut(int (A::*)()const);
TypeOut(int (A::*const)());
#ifdef __clang__
TypeOut(int (A::*)()&);
TypeOut(int (A::*)()&&);
#endif
TypeOut(F<int>);
TypeOut(G<3>);
TypeOut(H<F>);
TypeOut(I<G>);
TypeOut(J<int>);
TypeOut(K<3>);
TypeOut(L<F>);
TypeOut(M<G>);
TypeOut(N<F,F,F>);
TypeOut(O<N,N>);
}
This compiles and works (nearly) perfectly in GCC (4.8). However, GCC doesn't support the ref-qualifiers on member functions, so tried it in Clang as well (3.2) to see if it would work. It doesn't. It compiles fine, and runs without issue, but most of the template names are not demangled, and nor are the ref-qualified member functions.
Here is the GCC output from ideone (the code was tweaked slightly since ideone's version of GCC doesn't support nullptr or alias declarations). Notice the missing const qualifier in the function parameter. That's the only thing that didn't work as I'd hoped in GCC. It's the same in Clang, too, which I suppose isn't especially surprising. (What's surprising is that it's missing at all, not that GCC and Clang are consistent on it.)
Unfortunately, when compiling and running with Clang, the variadic templated classes could not be demangled. For example, J was mangled to 1JIJiEE
instead of the 1JIIiEE
that GCC chose.
Selected output from clang (showing only the ones that didn't work)
Type int (A::*)()&:
Mangled: M1AFivRE
Demangled: M1AFivRE
typeinfo<>: 19preserve_qualifiersIM1AFivREE
Type int (A::*)()&&:
Mangled: M1AFivOE
Demangled: M1AFivOE
typeinfo<>: 19preserve_qualifiersIM1AFivOEE
Type J<int>:
Mangled: 1JIJiEE
Demangled: 1JIJiEE
typeinfo<>: 19preserve_qualifiersI1JIJiEEE
Type K<3>:
Mangled: 1KIJLi3EEE
Demangled: 1KIJLi3EEE
typeinfo<>: 19preserve_qualifiersI1KIJLi3EEEE
Type L<F>:
Mangled: 1LIJ1FEE
Demangled: 1LIJ1FEE
typeinfo<>: 19preserve_qualifiersI1LIJ1FEEE
Type M<G>:
Mangled: 1MIJ1GEE
Demangled: 1MIJ1GEE
typeinfo<>: 19preserve_qualifiersI1MIJ1GEEE
Type N<F,F,F>:
Mangled: 1NIJ1FS0_S0_EE
Demangled: 1NIJ1FS0_S0_EE
typeinfo<>: 19preserve_qualifiersI1NIJ1FS1_S1_EEE
Type O<N,N>:
Mangled: 1OIJ1NS0_EE
Demangled: 1OIJ1NS0_EE
typeinfo<>: 19preserve_qualifiersI1OIJ1NS1_EEE
Does anyone have any idea how this might be resolved? Is it a bug in clang? (Or maybe a bug in GCC?) My first suspicion was a version incompatibility between the C++ ABI being linked with my code and the C++ ABI that clang is using. The fact that the issue only occurs with new C++11 features (member function ref-qualifiers and variadic templates) seems to support this suspicion... though r-value references (for example) don't have a problem.
In case it's relevant, I'm on Mac OSX Lion, and GCC 4.8 and clang 3.2 were installed from MacPorts. I'm using the following commands for compilation:
clang++-mp-3.2 -isystem/usr/local/include/c++/v1 -stdlib=libc++ -std=gnu++11 typeinfo-min.cpp -o typeinfofun-clang
G++-mp-4.8 -std=gnu++11 typeinfo-min.cpp -o typeinfofun-gcc
The isystem flag seems to be required in order to allow clang to find the libc++ headers (which was necessary when I was using <type_traits>
for the more complicated version of this). Without it, it can't even find <iostream>
.
(As an additional note, this issue does affect the more complicated version as well, since class names were — probably unsurprisingly — not among the specializations for complicated types, and thus just used the ABI demangle call.)
(Also as an aside, I'm curious about the portability of this. I know it's not necessary in MSVC since that returns the unmangled name from typeid(...).name()
, but apart from that...)
I've checked on gcc4.8 mingw3.0 Your example:
gcc version 4.8.1 20130324 (prerelease) (rubenvb-4.8-stdthread)
c++.exe -ggdb -Os -frtti -fexceptions -fpic -std=gnu++11
And I got that result
Type void(*volatile)(void(*const)()):
Mangled: PFvPFvvEE
Demangled: void (*)(void (*)())
typeinfo<>: void (* volatile)(void (*)())
Type int (A::*)():
Mangled: M1AFivE
Demangled: int (A::*)()
typeinfo<>: int (A::*)()
Type int (A::*)()const:
Mangled: M1AKFivE
Demangled: int (A::*)() const
typeinfo<>: int (A::*)() const
Type int (A::*const)():
Mangled: M1AFivE
Demangled: int (A::*)()
typeinfo<>: int (A::* const)()
Type F<int>:
Mangled: 1FIiE
Demangled: F<int>
typeinfo<>: F<int>
Type G<3>:
Mangled: 1GILi3EE
Demangled: G<3>
typeinfo<>: G<3>
Type H<F>:
Mangled: 1HI1FE
Demangled: H<F>
typeinfo<>: H<F>
Type I<G>:
Mangled: 1II1GE
Demangled: I<G>
typeinfo<>: I<G>
Type J<int>:
Mangled: 1JIIiEE
Demangled: J<int>
typeinfo<>: J<int>
Type K<3>:
Mangled: 1KIILi3EEE
Demangled: K<3>
typeinfo<>: K<3>
Type L<F>:
Mangled: 1LII1FEE
Demangled: L<F>
typeinfo<>: L<F>
Type M<G>:
Mangled: 1MII1GEE
Demangled: M<G>
typeinfo<>: M<G>
Type N<F,F,F>:
Mangled: 1NII1FS0_S0_EE
Demangled: N<F, F, F>
typeinfo<>: N<F, F, F>
Type O<N,N>:
Mangled: 1OII1NS0_EE
Demangled: O<N, N>
typeinfo<>: O<N, N>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With