Pretty-print types and class template along with all its template arguments
Asked Answered
I

1

12

Since typeid(T).name() doesn't return human understandable name of the type, it doesn't help us much if we want to print the name of the template arguments to some class template, especially when we're debugging. We often feel like writing this in debugging:

print<Args...>(cout); //dump the names of all types to stdout!

So I'm writing pretty-print utility which gives me the name of the class template. Well, it is easier to understand it through some sample usage:

print<int>(cout);               //prints int
print<int, double, char>(cout); //prints int, double, char
print<std::string>(cout);       //prints std::basic_string<char,  .. etc>
print<std::wstring>(cout);      //prints std::basic_string<wchar_t, .. etc>
print<X<int,Y<int>>>(cout);     //prints X<int, Y<int>>

Internally, I'm using a class template called template_name which returns me "Y" when I pass Y<int> to it as template argument. Here is how it is partially specialized for each user class template.

#define DEFINE_TEMPLATE_NAME(template_type) \
template<typename ... Ts>\
struct template_name<template_type<Ts...>>\
{\
    static const char* name()\
    {\
        return #template_type;\
    }\
};

And the user is required to use this macro to register his template class as:

DEFINE_TEMPLATE_NAME(std::basic_string);
DEFINE_TEMPLATE_NAME(std::vector);
DEFINE_TEMPLATE_NAME(X); //X is a class template
DEFINE_TEMPLATE_NAME(Y); //Y is a class template

That works because the specialization template_name<template_type<Ts...>> is a variadic class template on types only, which means it would return me the name of class template as long as all the template parameters are types. It is also able to print function-types and member-function-types as well:

typedef void fun(int,int);

//lets use snl::name() which returns name instead of printing!
std::cout << snl::name<fun>();    //prints : void(int,int)
std::cout << snl::name<fun*>();   //prints : void(*)(int,int)

Please see the working code here with other minute details. That works great so far.

But now I'm improving on this, and want to add support for non-types tempate arguments and mixed template arguments as well:

template<int...>
struct Z{};

//non-type template arguments : 1,2,3
snd::print<Z<1,2,3>>(cout);  //should print Z<1,2,3>

//mixed template arguments : int, 100
snd::print<std::array<int,100>>(cout);  //should print std::array<int,100>

How would I do that? How do I get the name of such class template and its arguments generically?

Infrasonic answered 30/12, 2012 at 15:45 Comment(2)
GCC has a demangler that prints pretty useful template class names...Lighthouse
@KerrekSB: Yes, but that is not standard. I want something that works everywhere.Infrasonic
C
6

I'm sorry this is a "negative answer" (I am upvoting your question), but I'm afraid you cannot do that. Even considering only template classes which accept homogeneous lists of non-type parameters (e.g. template<int, int>, template<char, char, char>, etc.), you would need a specialization of this kind:

template<typename T>
struct single_type
{
    // ...
};

template<typename U, template<U...> class C, U... Us>
struct single_type<C<Us...>>
{
    // ...
};

This specialization is legal but useless, because argument type U can never be deduced. You may define dedicated specializations for uniform lists of literals of the most common types (int..., char..., etc.), but it would still be impossible to cover sequences of heterogeneous types, let alone sequences of mixed arguments.

I'm afraid we'll have to wait for C++ to support reflection in order to achieve what you're looking for.

Curb answered 30/12, 2012 at 17:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.