2012-12-30 5 views
12

Da typeid(T).name() den menschlichen verständlichen Namen des Typs nicht zurückgibt, hilft es uns nicht viel, wenn wir den Namen der Vorlagenargumente zu irgendeiner Klasse drucken wollen Vorlage, vor allem beim Debuggen. Wir haben oft das Gefühl, dies beim Debuggen zu schreiben:Pretty-print-Typen und Klassenvorlage zusammen mit allen seinen Vorlagenargumenten

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

Also schreibe ich Pretty-Print-Dienstprogramm, das mir den Namen der Klassenvorlage gibt. Nun, ist es einfacher, es durch einige Probe Nutzung zu verstehen:

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

Intern, ich bin eine Klassenvorlage mit template_name genannt, die mich "Y" zurückkehrt, wenn ich Y<int> es als Template-Argument übergeben. Hier ist, wie es teilweise für jede Benutzerklassenvorlage spezialisiert ist.

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

und der Benutzer wird benötigt um dieses Makro zu verwenden, um seine Template-Klasse registrieren als:

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 

Das funktioniert, weil die Spezialisierung template_name<template_type<Ts...>> eine variadische Klasse-Vorlage auf Typen ist nur, was es bedeutet, würde mir den Namen der Klassenvorlage zurückgeben, solange alle Vorlagenparameter sind Typen. Es ist auch in der Lage funktions Typen und Mitglied Funktions-Typen sowie drucken:

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) 

Bitte sehen the working code here mit anderen kleinen Details. Das funktioniert bisher großartig.

Aber jetzt auf das ich verbessern und will, Unterstützung für Nicht-Typen Argumente und gemischte Vorlage Argumente tempate auch:

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> 

Wie würde ich das tun? Wie bekomme ich den Namen einer solchen Klassenvorlage und ihrer Argumente generisch?

+5

GCC hat einen Demaggler, der ziemlich nützliche Vorlagenklassennamen druckt ... –

+0

@KerrekSB: Ja, aber das ist kein Standard. Ich will etwas, das überall funktioniert. – Nawaz

Antwort

6

Es tut mir leid, das ist eine "negative Antwort" (Ich bin Ihre Frage aufheben), aber ich fürchte, Sie können das nicht tun. Auch nur Template-Klassen unter Berücksichtigung der homogenen Listen von nicht-Typ Parameter akzeptieren (zB template<int, int>, template<char, char, char>, etc.), würden Sie eine Spezialisierung dieser Art benötigen:

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

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

Diese Spezialisierung ist legal, aber nutzlos, weil Argument Typ U kann nie abgeleitet werden. Sie können dedizierte Spezialisierungen für einheitliche Listen von Literalen der gebräuchlichsten Typen definieren (int..., char..., usw.), aber es wäre immer noch unmöglich, Sequenzen von heterogenen Typen, geschweige denn Sequenzen von gemischten Argumenten zu erfassen.

Ich fürchte, wir müssen auf C++ warten, um die Reflexion zu unterstützen, um das zu erreichen, wonach Sie suchen.