Barry
Barry

Reputation: 302767

Possible to template on types OR non-types?

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?

Upvotes: 8

Views: 798

Answers (2)

user2249683
user2249683

Reputation:

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)

Upvotes: 7

Daniel Frey
Daniel Frey

Reputation: 56863

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.

Upvotes: 3

Related Questions