Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
254 views
in Technique[技术] by (71.8m points)

c++ - Pretty-print types and class template along with all its template arguments

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...