Hier ist eine Zusammenfassung von Howards Langformlösung, die jedoch mit einem ketzerischen einzeiligen Makro implementiert wurde : #define DEMANGLE_TYPEID_NAME(x) abi::__cxa_demangle(typeid((x)).name(), NULL, NULL, NULL). Wenn Sie plattformübergreifende Unterstützung benötigen: Verwenden Sie #ifdef, #else, #endifein Makros für andere Plattformen wie MSVC zu bieten.
Wenn Sie dies nur zum Debuggen verwenden, sollten Sie dies berücksichtigen template<typename T> void print_T() { std::cout << __PRETTY_FUNCTION__ << '\n'; }. Wenn Sie dann eg verwenden, print_T<const int * const **>();wird void print_T() [T = const int *const **]zur Laufzeit gedruckt und alle Qualifikationsmerkmale werden beibehalten (funktioniert in GCC und Clang).
Henri Menke
@ Henri, __PRETTY_FUNCTION__ist nicht Standard C ++ (Voraussetzung ist im Fragentitel ).
Toby Speight
Antworten:
505
C ++ 11 Update auf eine sehr alte Frage: Druckvariablentyp in C ++.
Die akzeptierte (und gut) Antwort ist die Verwendung typeid(a).name(), wo aein Variablenname.
Jetzt haben wir in C ++ 11 decltype(x), was einen Ausdruck in einen Typ verwandeln kann. Und decltype()kommt mit seinen eigenen sehr interessanten Regeln. Zum Beispiel decltype(a)und decltype((a))wird im Allgemeinen verschiedene Typen sein (und aus guten und verständlichen Gründen, sobald diese Gründe aufgedeckt sind).
Werden unsere vertrauenswürdigen typeid(a).name() uns helfen, diese schöne neue Welt zu erkunden?
Nein.
Aber das Werkzeug, das wird, ist nicht so kompliziert. Und es ist dieses Werkzeug, das ich als Antwort auf diese Frage benutze. Ich werde dieses neue Tool vergleichen und gegenüberstellen typeid(a).name(). Und dieses neue Tool baut tatsächlich darauf auftypeid(a).name() .
Das grundlegende Problem:
typeid(a).name()
wirft cv-qualifiers, referenzen und lvalue / rvalue-ness weg. Zum Beispiel:
constint ci =0;
std::cout <<typeid(ci).name()<<'\n';
Für mich Ausgaben:
i
und ich vermute auf MSVC-Ausgängen:
int
Dh das constist weg. Dies ist kein QOI-Problem (Quality of Implementation). Der Standard schreibt dieses Verhalten vor.
Was ich unten empfehle, ist:
template<typename T> std::string type_name();
welches so verwendet werden würde:
constint ci =0;
std::cout << type_name<decltype(ci)>()<<'\n';
und für mich Ausgaben:
intconst
<disclaimer>Ich habe dies nicht auf MSVC getestet. </disclaimer> Aber ich freue mich über Feedback von denen, die dies tun.
Die C ++ 11-Lösung
Ich verwende __cxa_demanglefür Nicht-MSVC-Plattformen, wie von ipapadop in seiner Antwort auf Demangle-Typen empfohlen. Aber bei MSVC vertraue ich darauf typeid, Namen zu entwirren (ungetestet). In diesem Kern geht es um einige einfache Tests, bei denen Lebenslaufqualifizierer und Verweise auf den Eingabetyp erkannt, wiederhergestellt und gemeldet werden.
#include<type_traits>#include<typeinfo>#ifndef _MSC_VER
# include <cxxabi.h>#endif#include<memory>#include<string>#include<cstdlib>template<class T>
std::string
type_name(){typedeftypename std::remove_reference<T>::type TR;
std::unique_ptr<char,void(*)(void*)> own
(#ifndef _MSC_VER
abi::__cxa_demangle(typeid(TR).name(),nullptr,nullptr,nullptr),#elsenullptr,#endif
std::free
);
std::string r = own !=nullptr? own.get():typeid(TR).name();if(std::is_const<TR>::value)
r +=" const";if(std::is_volatile<TR>::value)
r +=" volatile";if(std::is_lvalue_reference<T>::value)
r +="&";elseif(std::is_rvalue_reference<T>::value)
r +="&&";return r;}
Die Ergebnisse
Mit dieser Lösung kann ich Folgendes tun:
int& foo_lref();int&& foo_rref();int foo_value();int
main(){int i =0;constint ci =0;
std::cout <<"decltype(i) is "<< type_name<decltype(i)>()<<'\n';
std::cout <<"decltype((i)) is "<< type_name<decltype((i))>()<<'\n';
std::cout <<"decltype(ci) is "<< type_name<decltype(ci)>()<<'\n';
std::cout <<"decltype((ci)) is "<< type_name<decltype((ci))>()<<'\n';
std::cout <<"decltype(static_cast<int&>(i)) is "<< type_name<decltype(static_cast<int&>(i))>()<<'\n';
std::cout <<"decltype(static_cast<int&&>(i)) is "<< type_name<decltype(static_cast<int&&>(i))>()<<'\n';
std::cout <<"decltype(static_cast<int>(i)) is "<< type_name<decltype(static_cast<int>(i))>()<<'\n';
std::cout <<"decltype(foo_lref()) is "<< type_name<decltype(foo_lref())>()<<'\n';
std::cout <<"decltype(foo_rref()) is "<< type_name<decltype(foo_rref())>()<<'\n';
std::cout <<"decltype(foo_value()) is "<< type_name<decltype(foo_value())>()<<'\n';}
und die Ausgabe ist:
decltype(i) is intdecltype((i)) is int&decltype(ci) is intconstdecltype((ci)) is intconst&decltype(static_cast<int&>(i)) is int&decltype(static_cast<int&&>(i)) is int&&decltype(static_cast<int>(i)) is intdecltype(foo_lref()) is int&decltype(foo_rref()) is int&&decltype(foo_value()) is int
Beachten Sie (zum Beispiel) den Unterschied zwischen decltype(i)und decltype((i)). Ersteres ist die Art der Erklärung von i. Letzteres ist der "Typ" des Ausdrucksi . (Ausdrücke haben niemals einen Referenztyp, sondern stellen als Konvention decltypelWert-Ausdrücke mit lWert-Referenzen dar).
Somit ist dieses Tool ein hervorragendes Mittel, um etwas zu lernen decltypeund Ihren eigenen Code zu erkunden und zu debuggen.
Im Gegensatz dazu wäre typeid(a).name()die Ausgabe , wenn ich dies nur aufbauen würde , ohne verlorene Lebenslaufqualifizierer oder Referenzen wieder hinzuzufügen:
decltype(i) is intdecltype((i)) is intdecltype(ci) is intdecltype((ci)) is intdecltype(static_cast<int&>(i)) is intdecltype(static_cast<int&&>(i)) is intdecltype(static_cast<int>(i)) is intdecltype(foo_lref()) is intdecltype(foo_rref()) is intdecltype(foo_value()) is int
Dh jeder Referenz- und Lebenslaufqualifizierer wird entfernt.
C ++ 14 Update
Gerade wenn Sie glauben, eine Lösung für ein Problem gefunden zu haben, kommt immer jemand aus dem Nichts und zeigt Ihnen einen viel besseren Weg. :-)
Diese Antwort von Jamboree zeigt, wie der Typname in C ++ 14 zur Kompilierungszeit abgerufen wird. Es ist aus mehreren Gründen eine brillante Lösung:
Es ist zur Kompilierungszeit!
Sie veranlassen den Compiler selbst, den Job anstelle einer Bibliothek (sogar einer std :: lib) auszuführen. Dies bedeutet genauere Ergebnisse für die neuesten Sprachfunktionen (wie Lambdas).
Jamborees Antwort legt nicht alles für VS fest, und ich optimiere seinen Code ein wenig. Da diese Antwort jedoch viele Aufrufe erhält, nehmen Sie sich etwas Zeit, um ihre Antwort zu verbessern. Ohne diese Antwort wäre dieses Update niemals zustande gekommen.
Dieser Code wird automatisch zurückgesetzt, constexprwenn Sie noch im alten C ++ 11 stecken. Und wenn Sie mit C ++ 98/03 an die Höhlenwand malen, ist dienoexcept wird auch das geopfert.
C ++ 17 Update
In den Kommentaren unten weist Lyberta darauf hin, dass das Neue std::string_viewersetzen kann static_string:
template<class T>constexpr
std::string_view
type_name(){usingnamespace std;#ifdef __clang__
string_view p = __PRETTY_FUNCTION__;return string_view(p.data()+34, p.size()-34-1);#elif defined(__GNUC__)
string_view p = __PRETTY_FUNCTION__;# if __cplusplus < 201402return string_view(p.data()+36, p.size()-36-1);# elsereturn string_view(p.data()+49, p.find(';',49)-49);# endif#elif defined(_MSC_VER)
string_view p = __FUNCSIG__;return string_view(p.data()+84, p.size()-84-7);#endif}
Ich habe die Konstanten für VS dank der sehr schönen Detektivarbeit von Jive Dadson in den Kommentaren unten aktualisiert.
VS 14 CTP druckte die richtigen Typen aus, ich musste nur eine #include <iostream>Zeile hinzufügen .
Max Galkin
3
Warum Vorlage <Typname T> std :: string Typname ()? Warum übergeben Sie keinen Typ als Argument?
Moonman239
2
Ich glaube, meine Begründung war, dass ich manchmal nur einen Typ hatte (wie einen abgeleiteten Vorlagenparameter), und ich wollte keinen davon künstlich konstruieren müssen, um den Typ zu erhalten (obwohl diese Tage declvalden Job machen würden).
Howard Hinnant
5
@AngelusMortis: Da Englisch im Vergleich zu C ++ - Code vage / mehrdeutig ist, empfehle ich Ihnen, dies mit dem spezifischen Typ, an dem Sie interessiert sind, und mit dem spezifischen Compiler, an dem Sie interessiert sind, zu kopieren / in Ihren Testfall einzufügen und mit mehr zurückzuschreiben Details, wenn das Ergebnis überraschend und / oder unbefriedigend ist.
Howard Hinnant
3
@ HowardHinnant können Sie std::string_viewanstelle von verwenden static_string?
Möglicherweise müssen Sie RTTI in Ihren Compileroptionen aktivieren, damit dies funktioniert. Darüber hinaus hängt die Ausgabe davon vom Compiler ab. Es kann sich um einen rohen Typnamen oder ein Namensmangel-Symbol oder etwas dazwischen handeln.
Warum ist die von der Funktion name () zurückgegebene Zeichenfolge für die Implementierung definiert?
Destruktor
4
@PravasiMeet Soweit ich weiß, kein guter Grund. Das Komitee wollte Compiler-Implementierer einfach nicht in bestimmte technische Richtungen zwingen - im Nachhinein wahrscheinlich ein Fehler.
Konrad Rudolph
2
Gibt es ein Flag, mit dem ich RTTI aktivieren könnte? Vielleicht könnten Sie Ihre Antwort inklusive machen.
Jim
4
@Destructor Die Bereitstellung eines standardisierten Namensveränderungsformats kann den Eindruck erwecken, dass die Interoperabilität zwischen Binärdateien, die von zwei verschiedenen Compilern erstellt wurden, möglich und / oder sicher ist, wenn dies nicht der Fall ist. Da C ++ keinen Standard-ABI hat, wäre ein Standard-Namensmangelschema sinnlos und möglicherweise irreführend und gefährlich.
Elkvis
1
@ Jim Der Abschnitt über Compiler-Flags wäre eine Größenordnung länger als die Antwort selbst. GCC kompiliert standardmäßig mit dieser Option, daher "-fno-rtti". Andere Compiler entscheiden sich möglicherweise dagegen, aber es gibt keinen Standard für Compiler-Flags.
KFSONE
82
Sehr hässlich, macht aber den Trick, wenn Sie nur Informationen zur Kompilierungszeit benötigen (z. B. zum Debuggen):
auto testVar = std::make_tuple(1,1.0,"abc");decltype(testVar)::foo=1;
Kehrt zurück:
Compilation finished with errors:
source.cpp:In function 'int main()':
source.cpp:5:19: error:'foo' is not a member of 'std::tuple<int, double, const char*>'
Nur C ++ könnte dies so schwierig machen (Drucken eines automatischen Variablentyps zur Kompilierungszeit). NUR C ++.
Karl Pickett
3
@KarlP Nun, um fair zu sein, es ist ein wenig verworren, das funktioniert auch :) auto testVar = std::make_tuple(1, 1.0, "abc"); decltype(testVar)::foo = 1;
NickV
Unter VC ++ 17 reduziert dies eine rWert-Referenz auf eine einfache Referenz, selbst in einer Vorlagenfunktion mit Weiterleitungsreferenzparameter, und den in std :: forward eingeschlossenen Objektnamen.
Jive Dadson
Sie konnten zum Typ gelangen, ohne neue Räder zu erstellen!
Steven Eckhoff
1
Diese Technik wird auch in "Punkt 4: Wissen, wie abgeleitete Typen
angezeigt
54
Vergessen Sie nicht einzuschließen <typeinfo>
Ich glaube, Sie beziehen sich auf die Identifizierung des Laufzeit-Typs. Sie können das oben genannte erreichen, indem Sie dies tun.
Sie können diese Informationen also nicht für die Serialisierung verwenden. Die Eigenschaft typeid (a) .name () kann jedoch weiterhin für Protokoll- / Debugzwecke verwendet werden
Wird nicht "int" für Shorts und Zeichen gedruckt? Und "Float" für Doppel?
gartenriese
1
@gartenriese Spezialisierung hat diesen Nachteil nicht. Denn doublees würde die nicht spezialisierte Version der Vorlagenfunktion kompilieren, anstatt eine implizite Typkonvertierung
durchzuführen
1
@chappjc: Ich weiß ehrlich gesagt nicht, warum ich das damals gefragt habe, es ist mir jetzt ziemlich klar. Aber danke, dass du trotzdem eine einjährige Frage beantwortet hast!
gartenriese
2
@gartenriese Ich dachte mir das auch, aber "das Internet" könnte irgendwann die gleiche Frage haben.
Chappjc
18
Wie bereits erwähnt, typeid().name()kann ein verstümmelter Name zurückgegeben werden. In GCC (und einigen anderen Compilern) können Sie dies mit dem folgenden Code umgehen:
#include<cxxabi.h>#include<iostream>#include<typeinfo>#include<cstdlib>namespace some_namespace {namespace another_namespace {class my_class {};}}int main(){typedef some_namespace::another_namespace::my_class my_type;// mangled
std::cout <<typeid(my_type).name()<< std::endl;// unmangledint status =0;char* demangled = abi::__cxa_demangle(typeid(my_type).name(),0,0,&status);switch(status){case-1:{// could not allocate memory
std::cout <<"Could not allocate memory"<< std::endl;return-1;}break;case-2:{// invalid name under the C++ ABI mangling rules
std::cout <<"Invalid name"<< std::endl;return-1;}break;case-3:{// invalid argument
std::cout <<"Invalid argument to demangle()"<< std::endl;return-1;}break;}
std::cout << demangled << std::endl;
free(demangled);return0;
Das DECLARE_TYPE_NAME Definition dient dazu, Ihnen das Deklarieren dieser Merkmalsklasse für alle Typen zu erleichtern, die Sie voraussichtlich benötigen.
Dies ist möglicherweise nützlicher als die Lösungen, typeidda Sie die Ausgabe steuern können. Wenn Sie beispielsweise typeidfür long longmeinen Compiler verwenden, erhalten Sie "x".
In C ++ 11 haben wir decltype. In Standard-C ++ gibt es keine Möglichkeit, den genauen Variablentyp anzuzeigen, der mit decltype deklariert wurde. Wir können den Boost-Typeindex verwenden type_id_with_cvr(cvr steht für const, volatile, reference), um den Typ wie unten zu drucken.
#include<iostream>#include<boost/type_index.hpp>usingnamespace std;using boost::typeindex::type_id_with_cvr;int main(){int i =0;constint ci =0;
cout <<"decltype(i) is "<< type_id_with_cvr<decltype(i)>().pretty_name()<<'\n';
cout <<"decltype((i)) is "<< type_id_with_cvr<decltype((i))>().pretty_name()<<'\n';
cout <<"decltype(ci) is "<< type_id_with_cvr<decltype(ci)>().pretty_name()<<'\n';
cout <<"decltype((ci)) is "<< type_id_with_cvr<decltype((ci))>().pretty_name()<<'\n';
cout <<"decltype(std::move(i)) is "<< type_id_with_cvr<decltype(std::move(i))>().pretty_name()<<'\n';
cout <<"decltype(std::static_cast<int&&>(i)) is "<< type_id_with_cvr<decltype(static_cast<int&&>(i))>().pretty_name()<<'\n';return0;}
Hölle hässlich, aber wird für das tun, was ich brauche. Und viel kleiner als die anderen Lösungen. Funktioniert übrigens auf dem Mac.
Marco Luglio
6
Howard Hinnant verwendete magische Zahlen, um den Typnamen zu extrahieren. 康 桓 瑋 schlug ein Zeichenfolgenpräfix und -suffix vor. Aber Präfix / Suffix ändern sich ständig. Mit "probe_type" berechnet type_name automatisch die Präfix- und Suffixgrößen für "probe_type", um den Typnamen zu extrahieren:
test
constint*&unsignedintconstintconstint*constint*&constexpr std::string_view type_name()[with T = probe_type; std::string_view = std::basic_string_view<char>]
clang 10.0.0 Wandbox:
test
constint*&unsignedintconstintconstint*constint*&
std::__1::string_view type_name()[T = probe_type]
VS 2019 Version 16.3.3:
class test
constint*&unsignedintconstintconstint*constint*&class std::basic_string_view<char,struct std::char_traits<char>> __cdecl type_name<class probe_type>(void)
Die anderen Antworten mit RTTI (typeid) sind wahrscheinlich genau das, was Sie wollen, solange:
Sie können sich den Speicheraufwand leisten (der bei einigen Compilern beträchtlich sein kann).
Die Klassennamen, die Ihr Compiler zurückgibt, sind nützlich
Die Alternative (ähnlich wie bei Greg Hewgills Antwort) besteht darin, eine Tabelle mit Merkmalen zur Kompilierungszeit zu erstellen.
template<typename T>struct type_as_string;// declare your Wibble type (probably with definition of Wibble)template<>struct type_as_string<Wibble>{staticconstchar*const value ="Wibble";};
Beachten Sie, dass Sie beim Umschließen der Deklarationen in ein Makro aufgrund des Kommas Probleme haben, Namen für Vorlagentypen zu deklarieren, die mehr als einen Parameter (z. B. std :: map) verwenden.
Um auf den Namen des Variablentyps zuzugreifen, benötigen Sie lediglich
(i) es funktioniert nicht für andere Typen (dh überhaupt nicht generisch); (ii) nutzloses Aufblähen von Code; (iii) das gleiche kann (richtig) mit typeidoder gemacht werden decltype.
Edmz
2
Sie haben Recht, aber es deckt alle Grundtypen ab ... und das ist es, was ich gerade brauche ...
Jahid
2
Können Sie mir sagen, wie würden Sie es mit decltype tun,
Jahid
1
Wenn es sich um einen Test zur Kompilierungszeit handelt, können Sie std :: is_same <T, S> und decltype verwenden, um T und S zu erhalten.
edmz
4
Als ich mich herausforderte, beschloss ich zu testen, wie weit man mit plattformunabhängigen (hoffentlich) Template-Tricks gehen kann.
Die Namen werden zur Kompilierungszeit vollständig zusammengestellt. (Was bedeutet, typeid(T).name()dass nicht verwendet werden konnte, daher müssen Sie explizit Namen für nicht zusammengesetzte Typen angeben. Andernfalls werden stattdessen Platzhalter angezeigt.)
Anwendungsbeispiel:
TYPE_NAME(int)
TYPE_NAME(void)// You probably should list all primitive types here.
TYPE_NAME(std::string)int main(){// A simple case
std::cout << type_name<void(*)(int)><<'\n';// -> `void (*)(int)`// Ugly mess case// Note that compiler removes cv-qualifiers from parameters and replaces arrays with pointers.
std::cout << type_name<void(std::string::*(int[3],constint,void(*)(std::string)))(volatileint*const*)><<'\n';// -> `void (std::string::*(int *,int,void (*)(std::string)))(volatile int *const*)`// A case with undefined types// If a type wasn't TYPE_NAME'd, it's replaced by a placeholder, one of `class?`, `union?`, `enum?` or `??`.
std::cout << type_name<std::ostream (*)(int,short)><<'\n';// -> `class? (*)(int,??)`// With appropriate TYPE_NAME's, the output would be `std::string (*)(int,short)`.}
Code:
#include<type_traits>#include<utility>staticconstexpr std::size_t max_str_lit_len =256;template<std::size_t I, std::size_t N>constexprchar sl_at(constchar(&str)[N]){ifconstexpr(I < N)return str[I];elsereturn'\0';}constexpr std::size_t sl_len(constchar*str){for(std::size_t i =0; i < max_str_lit_len; i++)if(str[i]=='\0')return i;return0;}template<char...C>struct str_lit
{staticconstexprchar value[]{C...,'\0'};staticconstexprint size = sl_len(value);template<typename F,typename...P>struct concat_impl {using type =typename concat_impl<F>::type::template concat_impl<P...>::type;};template<char...CC>struct concat_impl<str_lit<CC...>>{using type = str_lit<C..., CC...>;};template<typename...P>using concat =typename concat_impl<P...>::type;};template<typename,constchar*>struct trim_str_lit_impl;template<std::size_t...I,constchar*S>struct trim_str_lit_impl<std::index_sequence<I...>, S>{using type = str_lit<S[I]...>;};template<std::size_t N,constchar*S>using trim_str_lit =typename trim_str_lit_impl<std::make_index_sequence<N>, S>::type;#define STR_LIT(str)::trim_str_lit<::sl_len(str),::str_lit<STR_TO_VA(str)>::value>#define STR_TO_VA(str) STR_TO_VA_16(str,0),STR_TO_VA_16(str,16),STR_TO_VA_16(str,32),STR_TO_VA_16(str,48)#define STR_TO_VA_16(str,off) STR_TO_VA_4(str,0+off),STR_TO_VA_4(str,4+off),STR_TO_VA_4(str,8+off),STR_TO_VA_4(str,12+off)#define STR_TO_VA_4(str,off)::sl_at<off+0>(str),::sl_at<off+1>(str),::sl_at<off+2>(str),::sl_at<off+3>(str)template<char...C>constexpr str_lit<C...> make_str_lit(str_lit<C...>){return{};}template<std::size_t N>constexprauto make_str_lit(constchar(&str)[N]){return trim_str_lit<sl_len((constchar(&)[N])str), str>{};}template<std::size_t A, std::size_t B>struct cexpr_pow {staticconstexpr std::size_t value = A * cexpr_pow<A,B-1>::value;};template<std::size_t A>struct cexpr_pow<A,0>{staticconstexpr std::size_t value =1;};template<std::size_t N, std::size_t X,typename= std::make_index_sequence<X>>struct num_to_str_lit_impl;template<std::size_t N, std::size_t X, std::size_t...Seq>struct num_to_str_lit_impl<N, X, std::index_sequence<Seq...>>{staticconstexprauto func(){ifconstexpr(N >= cexpr_pow<10,X>::value)return num_to_str_lit_impl<N, X+1>::func();elsereturn str_lit<(N / cexpr_pow<10,X-1-Seq>::value %10+'0')...>{};}};template<std::size_t N>using num_to_str_lit =decltype(num_to_str_lit_impl<N,1>::func());using spa = str_lit<' '>;using lpa = str_lit<'('>;using rpa = str_lit<')'>;using lbr = str_lit<'['>;using rbr = str_lit<']'>;using ast = str_lit<'*'>;using amp = str_lit<'&'>;using con = str_lit<'c','o','n','s','t'>;using vol = str_lit<'v','o','l','a','t','i','l','e'>;using con_vol = con::concat<spa, vol>;using nsp = str_lit<':',':'>;using com = str_lit<','>;using unk = str_lit<'?','?'>;using c_cla = str_lit<'c','l','a','s','s','?'>;using c_uni = str_lit<'u','n','i','o','n','?'>;using c_enu = str_lit<'e','n','u','m','?'>;template<typename T>inlineconstexprbool ptr_or_ref = std::is_pointer_v<T>|| std::is_reference_v<T>|| std::is_member_pointer_v<T>;template<typename T>inlineconstexprbool func_or_arr = std::is_function_v<T>|| std::is_array_v<T>;template<typename T>struct primitive_type_name {using value = unk;};template<typename T,typename= std::enable_if_t<std::is_class_v<T>>>using enable_if_class = T;template<typename T,typename= std::enable_if_t<std::is_union_v<T>>>using enable_if_union = T;template<typename T,typename= std::enable_if_t<std::is_enum_v <T>>>using enable_if_enum = T;template<typename T>struct primitive_type_name<enable_if_class<T>>{using value = c_cla;};template<typename T>struct primitive_type_name<enable_if_union<T>>{using value = c_uni;};template<typename T>struct primitive_type_name<enable_if_enum <T>>{using value = c_enu;};template<typename T>struct type_name_impl;template<typename T>using type_name_lit = std::conditional_t<std::is_same_v<typename primitive_type_name<T>::value::template concat<spa>,typename type_name_impl<T>::l::template concat<typename type_name_impl<T>::r>>,typename primitive_type_name<T>::value,typename type_name_impl<T>::l::template concat<typename type_name_impl<T>::r>>;template<typename T>inlineconstexprconstchar*type_name = type_name_lit<T>::value;template<typename T,typename= std::enable_if_t<!std::is_const_v<T>&&!std::is_volatile_v<T>>>using enable_if_no_cv = T;template<typename T>struct type_name_impl
{using l =typename primitive_type_name<T>::value::template concat<spa>;using r = str_lit<>;};template<typename T>struct type_name_impl<const T>{using new_T_l = std::conditional_t<type_name_impl<T>::l::size &&!ptr_or_ref<T>,
spa::concat<typename type_name_impl<T>::l>,typename type_name_impl<T>::l>;using l = std::conditional_t<ptr_or_ref<T>,typename new_T_l::template concat<con>,
con::concat<new_T_l>>;using r =typename type_name_impl<T>::r;};template<typename T>struct type_name_impl<volatile T>{using new_T_l = std::conditional_t<type_name_impl<T>::l::size &&!ptr_or_ref<T>,
spa::concat<typename type_name_impl<T>::l>,typename type_name_impl<T>::l>;using l = std::conditional_t<ptr_or_ref<T>,typename new_T_l::template concat<vol>,
vol::concat<new_T_l>>;using r =typename type_name_impl<T>::r;};template<typename T>struct type_name_impl<constvolatile T>{using new_T_l = std::conditional_t<type_name_impl<T>::l::size &&!ptr_or_ref<T>,
spa::concat<typename type_name_impl<T>::l>,typename type_name_impl<T>::l>;using l = std::conditional_t<ptr_or_ref<T>,typename new_T_l::template concat<con_vol>,
con_vol::concat<new_T_l>>;using r =typename type_name_impl<T>::r;};template<typename T>struct type_name_impl<T *>{using l = std::conditional_t<func_or_arr<T>,typename type_name_impl<T>::l::template concat<lpa, ast>,typename type_name_impl<T>::l::template concat< ast>>;using r = std::conditional_t<func_or_arr<T>,
rpa::concat<typename type_name_impl<T>::r>,typename type_name_impl<T>::r>;};template<typename T>struct type_name_impl<T &>{using l = std::conditional_t<func_or_arr<T>,typename type_name_impl<T>::l::template concat<lpa, amp>,typename type_name_impl<T>::l::template concat< amp>>;using r = std::conditional_t<func_or_arr<T>,
rpa::concat<typename type_name_impl<T>::r>,typename type_name_impl<T>::r>;};template<typename T>struct type_name_impl<T &&>{using l = std::conditional_t<func_or_arr<T>,typename type_name_impl<T>::l::template concat<lpa, amp, amp>,typename type_name_impl<T>::l::template concat< amp, amp>>;using r = std::conditional_t<func_or_arr<T>,
rpa::concat<typename type_name_impl<T>::r>,typename type_name_impl<T>::r>;};template<typename T,typename C>struct type_name_impl<T C::*>{using l = std::conditional_t<func_or_arr<T>,typename type_name_impl<T>::l::template concat<lpa, type_name_lit<C>, nsp, ast>,typename type_name_impl<T>::l::template concat< type_name_lit<C>, nsp, ast>>;using r = std::conditional_t<func_or_arr<T>,
rpa::concat<typename type_name_impl<T>::r>,typename type_name_impl<T>::r>;};template<typename T>struct type_name_impl<enable_if_no_cv<T[]>>{using l =typename type_name_impl<T>::l;using r = lbr::concat<rbr,typename type_name_impl<T>::r>;};template<typename T, std::size_t N>struct type_name_impl<enable_if_no_cv<T[N]>>{using l =typename type_name_impl<T>::l;using r = lbr::concat<num_to_str_lit<N>, rbr,typename type_name_impl<T>::r>;};template<typename T>struct type_name_impl<T()>{using l =typename type_name_impl<T>::l;using r = lpa::concat<rpa,typename type_name_impl<T>::r>;};template<typename T,typename P1,typename...P>struct type_name_impl<T(P1, P...)>{using l =typename type_name_impl<T>::l;using r = lpa::concat<type_name_lit<P1>,
com::concat<type_name_lit<P>>..., rpa,typename type_name_impl<T>::r>;};#define TYPE_NAME(t)template<>struct primitive_type_name<t>{using value = STR_LIT(#t);};
#include<iostream>#include<typeinfo>usingnamespace std;#define show_type_name(_t) \
system(("echo "+ string(typeid(_t).name())+" | c++filt -t").c_str())int main(){auto a ={"one","two","three"};
cout <<"Type of a: "<<typeid(a).name()<< endl;
cout <<"Real type of a:\n";
show_type_name(a);for(auto s : a){if(string(s)=="one"){
cout <<"Type of s: "<<typeid(s).name()<< endl;
cout <<"Real type of s:\n";
show_type_name(s);}
cout << s << endl;}int i =5;
cout <<"Type of i: "<<typeid(i).name()<< endl;
cout <<"Real type of i:\n";
show_type_name(i);return0;}
Ausgabe:
Type of a:St16initializer_listIPKcEReal type of a:
std::initializer_list<charconst*>Type of s:PKcReal type of s:charconst*
one
two
three
Type of i: i
Real type of i:int
Wie von Scott Meyers in Effective Modern C ++ erklärt,
Anrufe an std::type_info::namewerden nicht garantiert vernünftig zurückgegeben.
Die beste Lösung besteht darin, den Compiler während des Typabzugs eine Fehlermeldung generieren zu lassen, z.
template<typename T>class TD;int main(){constint theAnswer =32;auto x = theAnswer;auto y =&theAnswer;
TD<decltype(x)> xType;
TD<decltype(y)> yType;return0;}
Das Ergebnis wird ungefähr so aussehen, abhängig von den Compilern.
test4.cpp:10:21: error: aggregate ‘TD<int> xType’ has incomplete type and cannot be defined TD<decltype(x)> xType;
test4.cpp:11:21: error: aggregate ‘TD<constint*> yType’ has incomplete type and cannot be defined TD<decltype(y)> yType;
Daher erfahren wir, dass xder Typ ist int, yder Typ istconst int*
Für alle, die noch zu Besuch sind, hatte ich kürzlich das gleiche Problem und habe beschlossen, eine kleine Bibliothek zu schreiben, die auf den Antworten aus diesem Beitrag basiert. Es enthält constexpr-Typnamen und Typindizes und wird unter Mac, Windows und Ubuntu getestet.
#define DEMANGLE_TYPEID_NAME(x) abi::__cxa_demangle(typeid((x)).name(), NULL, NULL, NULL)
. Wenn Sie plattformübergreifende Unterstützung benötigen: Verwenden Sie#ifdef
,#else
,#endif
ein Makros für andere Plattformen wie MSVC zu bieten.template<typename T> void print_T() { std::cout << __PRETTY_FUNCTION__ << '\n'; }
. Wenn Sie dann eg verwenden,print_T<const int * const **>();
wirdvoid print_T() [T = const int *const **]
zur Laufzeit gedruckt und alle Qualifikationsmerkmale werden beibehalten (funktioniert in GCC und Clang).__PRETTY_FUNCTION__
ist nicht Standard C ++ (Voraussetzung ist im Fragentitel ).Antworten:
C ++ 11 Update auf eine sehr alte Frage: Druckvariablentyp in C ++.
Die akzeptierte (und gut) Antwort ist die Verwendung
typeid(a).name()
, woa
ein Variablenname.Jetzt haben wir in C ++ 11
decltype(x)
, was einen Ausdruck in einen Typ verwandeln kann. Unddecltype()
kommt mit seinen eigenen sehr interessanten Regeln. Zum Beispieldecltype(a)
unddecltype((a))
wird im Allgemeinen verschiedene Typen sein (und aus guten und verständlichen Gründen, sobald diese Gründe aufgedeckt sind).Werden unsere vertrauenswürdigen
typeid(a).name()
uns helfen, diese schöne neue Welt zu erkunden?Nein.
Aber das Werkzeug, das wird, ist nicht so kompliziert. Und es ist dieses Werkzeug, das ich als Antwort auf diese Frage benutze. Ich werde dieses neue Tool vergleichen und gegenüberstellen
typeid(a).name()
. Und dieses neue Tool baut tatsächlich darauf auftypeid(a).name()
.Das grundlegende Problem:
wirft cv-qualifiers, referenzen und lvalue / rvalue-ness weg. Zum Beispiel:
Für mich Ausgaben:
und ich vermute auf MSVC-Ausgängen:
Dh das
const
ist weg. Dies ist kein QOI-Problem (Quality of Implementation). Der Standard schreibt dieses Verhalten vor.Was ich unten empfehle, ist:
welches so verwendet werden würde:
und für mich Ausgaben:
<disclaimer>
Ich habe dies nicht auf MSVC getestet.</disclaimer>
Aber ich freue mich über Feedback von denen, die dies tun.Die C ++ 11-Lösung
Ich verwende
__cxa_demangle
für Nicht-MSVC-Plattformen, wie von ipapadop in seiner Antwort auf Demangle-Typen empfohlen. Aber bei MSVC vertraue ich darauftypeid
, Namen zu entwirren (ungetestet). In diesem Kern geht es um einige einfache Tests, bei denen Lebenslaufqualifizierer und Verweise auf den Eingabetyp erkannt, wiederhergestellt und gemeldet werden.Die Ergebnisse
Mit dieser Lösung kann ich Folgendes tun:
und die Ausgabe ist:
Beachten Sie (zum Beispiel) den Unterschied zwischen
decltype(i)
unddecltype((i))
. Ersteres ist die Art der Erklärung voni
. Letzteres ist der "Typ" des Ausdrucksi
. (Ausdrücke haben niemals einen Referenztyp, sondern stellen als Konventiondecltype
lWert-Ausdrücke mit lWert-Referenzen dar).Somit ist dieses Tool ein hervorragendes Mittel, um etwas zu lernen
decltype
und Ihren eigenen Code zu erkunden und zu debuggen.Im Gegensatz dazu wäre
typeid(a).name()
die Ausgabe , wenn ich dies nur aufbauen würde , ohne verlorene Lebenslaufqualifizierer oder Referenzen wieder hinzuzufügen:Dh jeder Referenz- und Lebenslaufqualifizierer wird entfernt.
C ++ 14 Update
Gerade wenn Sie glauben, eine Lösung für ein Problem gefunden zu haben, kommt immer jemand aus dem Nichts und zeigt Ihnen einen viel besseren Weg. :-)
Diese Antwort von Jamboree zeigt, wie der Typname in C ++ 14 zur Kompilierungszeit abgerufen wird. Es ist aus mehreren Gründen eine brillante Lösung:
Jamborees Antwort legt nicht alles für VS fest, und ich optimiere seinen Code ein wenig. Da diese Antwort jedoch viele Aufrufe erhält, nehmen Sie sich etwas Zeit, um ihre Antwort zu verbessern. Ohne diese Antwort wäre dieses Update niemals zustande gekommen.
Dieser Code wird automatisch zurückgesetzt,
constexpr
wenn Sie noch im alten C ++ 11 stecken. Und wenn Sie mit C ++ 98/03 an die Höhlenwand malen, ist dienoexcept
wird auch das geopfert.C ++ 17 Update
In den Kommentaren unten weist Lyberta darauf hin, dass das Neue
std::string_view
ersetzen kannstatic_string
:Ich habe die Konstanten für VS dank der sehr schönen Detektivarbeit von Jive Dadson in den Kommentaren unten aktualisiert.
Aktualisieren:
Schauen Sie sich unbedingt diese unten stehende Umschreibung an, die die unlesbaren magischen Zahlen in meiner neuesten Formulierung beseitigt.
quelle
#include <iostream>
Zeile hinzufügen .declval
den Job machen würden).std::string_view
anstelle von verwendenstatic_string
?Versuchen:
Möglicherweise müssen Sie RTTI in Ihren Compileroptionen aktivieren, damit dies funktioniert. Darüber hinaus hängt die Ausgabe davon vom Compiler ab. Es kann sich um einen rohen Typnamen oder ein Namensmangel-Symbol oder etwas dazwischen handeln.
quelle
Sehr hässlich, macht aber den Trick, wenn Sie nur Informationen zur Kompilierungszeit benötigen (z. B. zum Debuggen):
Kehrt zurück:
quelle
auto testVar = std::make_tuple(1, 1.0, "abc"); decltype(testVar)::foo = 1;
Vergessen Sie nicht einzuschließen
<typeinfo>
Ich glaube, Sie beziehen sich auf die Identifizierung des Laufzeit-Typs. Sie können das oben genannte erreichen, indem Sie dies tun.
quelle
Laut Howards Lösung ist dies eine gute Art der Darstellung und sieht intuitiv aus, wenn Sie die magische Zahl nicht wollen:
quelle
Beachten Sie, dass die von der RTTI-Funktion von C ++ generierten Namen nicht portierbar sind. Zum Beispiel die Klasse
wird die folgenden Namen haben:
Sie können diese Informationen also nicht für die Serialisierung verwenden. Die Eigenschaft typeid (a) .name () kann jedoch weiterhin für Protokoll- / Debugzwecke verwendet werden
quelle
Sie können Vorlagen verwenden.
Wenn der Typ im obigen Beispiel nicht übereinstimmt, wird "unbekannt" ausgegeben.
quelle
double
es würde die nicht spezialisierte Version der Vorlagenfunktion kompilieren, anstatt eine implizite TypkonvertierungWie bereits erwähnt,
typeid().name()
kann ein verstümmelter Name zurückgegeben werden. In GCC (und einigen anderen Compilern) können Sie dies mit dem folgenden Code umgehen:}}
quelle
Sie können hierfür eine Merkmalsklasse verwenden. Etwas wie:
Das
DECLARE_TYPE_NAME
Definition dient dazu, Ihnen das Deklarieren dieser Merkmalsklasse für alle Typen zu erleichtern, die Sie voraussichtlich benötigen.Dies ist möglicherweise nützlicher als die Lösungen,
typeid
da Sie die Ausgabe steuern können. Wenn Sie beispielsweisetypeid
fürlong long
meinen Compiler verwenden, erhalten Sie "x".quelle
In C ++ 11 haben wir decltype. In Standard-C ++ gibt es keine Möglichkeit, den genauen Variablentyp anzuzeigen, der mit decltype deklariert wurde. Wir können den Boost-Typeindex verwenden
type_id_with_cvr
(cvr steht für const, volatile, reference), um den Typ wie unten zu drucken.quelle
template<typename T> void print_type(T){cout << "type T is: "<< type_id_with_cvr<T>().pretty_name()<< '\n';}
Sie können auch c ++ filt mit der Option -t (Typ) verwenden, um den Typnamen zu entwirren:
Nur unter Linux getestet.
quelle
Howard Hinnant verwendete magische Zahlen, um den Typnamen zu extrahieren. 康 桓 瑋 schlug ein Zeichenfolgenpräfix und -suffix vor. Aber Präfix / Suffix ändern sich ständig. Mit "probe_type" berechnet type_name automatisch die Präfix- und Suffixgrößen für "probe_type", um den Typnamen zu extrahieren:
Ausgabe
gcc 10.0.0 20190919 Wandbox:
clang 10.0.0 Wandbox:
VS 2019 Version 16.3.3:
quelle
Die anderen Antworten mit RTTI (typeid) sind wahrscheinlich genau das, was Sie wollen, solange:
Die Alternative (ähnlich wie bei Greg Hewgills Antwort) besteht darin, eine Tabelle mit Merkmalen zur Kompilierungszeit zu erstellen.
Beachten Sie, dass Sie beim Umschließen der Deklarationen in ein Makro aufgrund des Kommas Probleme haben, Namen für Vorlagentypen zu deklarieren, die mehr als einen Parameter (z. B. std :: map) verwenden.
Um auf den Namen des Variablentyps zuzugreifen, benötigen Sie lediglich
quelle
Eine allgemeinere Lösung ohne Funktionsüberlastung als meine vorherige:
Hier ist MyClass eine benutzerdefinierte Klasse. Weitere Bedingungen können auch hier hinzugefügt werden.
Beispiel:
Ausgabe:
quelle
Ich mag Nicks Methode. Ein vollständiges Formular könnte dies sein (für alle grundlegenden Datentypen):
quelle
typeid
oder gemacht werdendecltype
.Als ich mich herausforderte, beschloss ich zu testen, wie weit man mit plattformunabhängigen (hoffentlich) Template-Tricks gehen kann.
Die Namen werden zur Kompilierungszeit vollständig zusammengestellt. (Was bedeutet,
typeid(T).name()
dass nicht verwendet werden konnte, daher müssen Sie explizit Namen für nicht zusammengesetzte Typen angeben. Andernfalls werden stattdessen Platzhalter angezeigt.)Anwendungsbeispiel:
Code:
quelle
Ausgabe:
quelle
Wie von Scott Meyers in Effective Modern C ++ erklärt,
Die beste Lösung besteht darin, den Compiler während des Typabzugs eine Fehlermeldung generieren zu lassen, z.
Das Ergebnis wird ungefähr so aussehen, abhängig von den Compilern.
Daher erfahren wir, dass
x
der Typ istint
,y
der Typ istconst int*
quelle
Für alle, die noch zu Besuch sind, hatte ich kürzlich das gleiche Problem und habe beschlossen, eine kleine Bibliothek zu schreiben, die auf den Antworten aus diesem Beitrag basiert. Es enthält constexpr-Typnamen und Typindizes und wird unter Mac, Windows und Ubuntu getestet.
Der Bibliothekscode ist hier: https://github.com/TheLartians/StaticTypeInfo
quelle