Красиво отпечатани типове и шаблон на клас заедно с всички негови аргументи на шаблона

Тъй като typeid(T).name() не връща разбираемо за човека име на типа, това не ни помага много, ако искаме да отпечатаме името на аргументите на шаблона към някакъв шаблон на клас, особено когато отстраняваме грешки. Често ни се иска да напишем това при отстраняване на грешки:

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

Така че пиша помощна програма pretty-print, която ми дава името на шаблона на класа. Е, по-лесно е да го разберете чрез примерна употреба:

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>>

Вътрешно използвам шаблон на клас, наречен template_name, който ми връща "Y", когато му предам Y<int> като аргумент на шаблона. Ето как е частично специализиран за всеки шаблон на потребителски клас.

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

И от потребителя се изисква да използва този макрос, за да регистрира своя шаблонен клас като:

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

Това работи, защото специализацията template_name<template_type<Ts...>> е променлив шаблон на клас само за типове, което означава, че ще ми върне името на шаблона на класа, стига всички параметри на шаблона да са типове. Той също така може да отпечатва типове функции и типове функции-членове:

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)

Моля, вижте работещия код тук с други подробности. Това работи чудесно досега.

Но сега подобрявам това и искам да добавя поддръжка и за шаблонни аргументи без типове и смесени шаблонни аргументи:

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>

Как бих направил това? Как да получа общо името на такъв шаблон на клас и неговите аргументи?


person Nawaz    schedule 30.12.2012    source източник
comment
GCC има деманглер, който отпечатва доста полезни имена на класове на шаблони...   -  person Kerrek SB    schedule 30.12.2012
comment
@KerrekSB: Да, но това не е стандартно. Искам нещо, което работи навсякъде.   -  person Nawaz    schedule 30.12.2012


Отговори (1)


Съжалявам, че това е „отрицателен отговор“ (гласувам за вашия въпрос), но се страхувам, че не можете да направите това. Дори като се имат предвид само шаблонни класове, които приемат хомогенни списъци с параметри без тип (напр. template<int, int>, template<char, char, char> и т.н.), ще ви е необходима специализация от този вид:

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

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

Тази специализация е законна, но безполезна, тъй като тип аргумент U никога не може да бъде изведен. Можете да дефинирате специални специализации за унифицирани списъци с литерали от най-често срещаните типове (int..., char... и т.н.), но пак би било невъзможно да се покрият поредици от разнородни типове, да не говорим за поредици от смесени аргументи.

Страхувам се, че ще трябва да изчакаме C++ да поддържа отражение, за да постигнем това, което търсите.

person Andy Prowl    schedule 30.12.2012