8

I use a lot of templates and it's occasionally hard to figure out just what type everything actually is. I wanted to write a utility to give me a nice, pretty string name for every type - typeid() just doesn't cut it. For instance, if I just have a vector<int>, gcc.4.6.4 on my box produces the following with typeid:

St6vectorIiSaIiEE

while I would ideally want

std::vector<
    int,
    std::allocator<
        int
    >
>

I have written something that will work with any type or template on types, but just providing two templates:

template <typename T> struct simple_type_name;
template <template <typename....> class T> struct template_type_name;

Which when specialized on int or std::vector can help me build up the strings I want. I also have a partial specialization of simple_type_name on just any Base<Args...> to walk through all the args and do everything as appropriate. This works totally fine for int and vector<int> and really any arbitrarily complicated template stuff... as long as all the templates are types.

If it helps, my "full template" version looks like this:

template <template <typename...> class Base, typename... Args>
struct simple_type_name<Base<Args...>>
{
    static std::string name(int indent = 0) {
        std::string base = template_type_name<Base>::name(indent);
        std::string args[] = { simple_type_name<Args>::name(indent + 4)... } ;

        // basic string putting together stuff here that is less interesting
    }
};

Question is: how do I make what I have work for, say, std::array<int, 10>? I do not know how to handle the non-type parameters. Is this even possible?

Barry
  • 286,269
  • 29
  • 621
  • 977
  • 4
    "I use a lot of templates and it's occasionally hard to figure out just what type everything actually is." I love C++ – Andrey Mishchenko Dec 16 '13 at 16:14
  • I remember that there should be a function available (at least for GCC) that is able to demangle the `typeid().name` for you. – πάντα ῥεῖ Dec 16 '13 at 16:15
  • 2
    Have you tried using `c++filt` to unmangle the names? – greatwolf Dec 16 '13 at 16:20
  • I've found s.th. relevant (to my former comment) on SO: http://stackoverflow.com/questions/281818/unmangling-the-result-of-stdtype-infoname – πάντα ῥεῖ Dec 16 '13 at 16:24
  • @Andrey: Theorem: For every programming language L, there is a construction such that you _love_ L. – Sebastian Mach Dec 16 '13 at 16:24
  • 2
    @Andrey: Btw: From your tag-set I see you are more on the C-side of things. Let me do a tiny transformation of the initial statement: "I use a lot of generic functions taking untyped pointers and callbacks and it's occasionally hard to figure out at runtime just what type everything actually is". Theorem proven. – Sebastian Mach Dec 16 '13 at 16:37
  • @phresnel I agree that every language has problems, but I find this particular situation to be quite ironic, as opposed to your C example which is an unfortunate side-effect (which *NOBODY CELEBRATES*) of a very low-level language designed to solve low-level problems. C++ templates are often *CELEBRATED* and *ENCOURAGED*, which is (IMO) hilarious given this post. – Andrey Mishchenko Dec 16 '13 at 17:14
  • 1
    @Andrey: Yeah, there's praise here and there. But then, I remember multiple times where the C-qsort-function was celebrated by class mates or even working comrades. I think the one thing they are most famous for is their turing completeness; just the generic programming part is not so much celebrated, imho, especially not in times where every new programming language has such. – Sebastian Mach Dec 16 '13 at 20:15

2 Answers2

7

If you like to have an g++ specific demangle:

#include <iostream>
#include <typeinfo>
#include <cxxabi.h>

std::string demangle(const std::string& source_name)
{
    std::string result;
    size_t size = 4096;
    // __cxa_demangle may realloc()
    char* name = static_cast<char*>(malloc(size));
    try {
        int status;
        char* demangle = abi::__cxa_demangle(source_name.c_str(), name, &size, &status);
        if(demangle) result = demangle;
        else result = source_name;
    }
    catch(...) {}
    free(name);
    return result;
}

template <typename T, int I> struct X {};
int main()
{
    // Prints: X<int, 0>
    std::cout << demangle(typeid(X<int, 0>).name()) << std::endl;
}

(Added a try/catch - Thanks to Daniel Frey)

3

A somewhat easier version of demangle with some convenience wrappers:

#include <string>
#include <memory>
#include <typeinfo>
#include <cxxabi.h>

std::string demangle( const char* symbol )
{
    const std::unique_ptr< char, decltype( &std::free ) > demangled( abi::__cxa_demangle( symbol, 0, 0, 0 ), &std::free );
    return demangled ? demangled : symbol;
}

std::string demangle( const std::string& symbol )
{
    return demangle( symbol.c_str() );
}

std::string demangle( const std::type_info& ti )
{
    return demangle( ti.name() );
}

which allows you to use:

std::cout << demangle( typeid( T ) ) << std::endl;

directly to see what T actually is.

Daniel Frey
  • 55,810
  • 13
  • 122
  • 180