Oto podsumowanie roztworu długo postać Howarda, ale realizowane z heretyckiego jedna linia makro: #define DEMANGLE_TYPEID_NAME(x) abi::__cxa_demangle(typeid((x)).name(), NULL, NULL, NULL). Jeśli potrzebujesz wsparcia cross-platform: Użyj #ifdef, #else, #endifw celu dostarczenia jednego makra dla innych platform, takich jak MSVC.
Jeśli używasz tego tylko do debugowania, możesz rozważyć template<typename T> void print_T() { std::cout << __PRETTY_FUNCTION__ << '\n'; }. Następnie użycie np. print_T<const int * const **>();Wydrukuje void print_T() [T = const int *const **]w czasie wykonywania i zachowa wszystkie kwalifikatory (działa w GCC i Clang).
Henri Menke
@Henri, __PRETTY_FUNCTION__nie jest Standardowym C ++ (wymaganie jest w tytule pytania).
Toby Speight
Odpowiedzi:
505
Aktualizacja C ++ 11 do bardzo starego pytania: wypisz typ zmiennej w C ++.
Zaakceptowaną (i dobrą) odpowiedzią jest użycie typeid(a).name(), gdzie ajest nazwą zmiennej.
Teraz w C ++ 11 mamy decltype(x), który może zmienić wyrażenie w typ. I decltype()ma własny zestaw bardzo interesujących zasad. Na przykład decltype(a)i na decltype((a))ogół będą to różne typy (oraz z dobrych i zrozumiałych powodów po ujawnieniu tych powodów).
Czy nasze zaufane rzeczy typeid(a).name()pomogą nam odkryć ten nowy wspaniały świat?
Nie.
Ale narzędzie, które nie będzie tak skomplikowane. I to jest to narzędzie, którego używam jako odpowiedź na to pytanie. Porównuję i skontrastuję to nowe narzędzie typeid(a).name(). A to nowe narzędzie jest w rzeczywistości zbudowane na bazie typeid(a).name().
Podstawowa kwestia:
typeid(a).name()
wyrzuca kwalifikatory cv, referencje i lvalue / rvalue-ness. Na przykład:
constint ci =0;
std::cout <<typeid(ci).name()<<'\n';
Dla mnie wyniki:
i
i zgaduję na wyjściach MSVC:
int
Tj const. Zniknął. To nie jest kwestia QOI (Quality of Implementation). Norma nakazuje takie zachowanie.
To co polecam poniżej to:
template<typename T> std::string type_name();
który byłby użyty w ten sposób:
constint ci =0;
std::cout << type_name<decltype(ci)>()<<'\n';
a dla mnie wyniki:
intconst
<disclaimer>Nie testowałem tego na MSVC. </disclaimer> Ale cieszę się z opinii tych, którzy to robią.
Rozwiązanie C ++ 11
Korzystam __cxa_demanglez platform innych niż MSVC, jak zalecił ipapadop w swojej odpowiedzi na typy trójkątów . Ale w MSVC ufam, typeidże rozplątam nazwy (niesprawdzone). I ten rdzeń jest otoczony kilkoma prostymi testami, które wykrywają, przywracają i raportują kwalifikatory cv oraz odniesienia do typu danych wejściowych.
#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;}
Wyniki
Dzięki temu rozwiązaniu mogę to zrobić:
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';}
a wynikiem jest:
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
Uwaga (na przykład) różnica między decltype(i)i decltype((i)). Były to rodzaj deklaracji o i. Ten ostatni jest „typem” wyrażeniai . (wyrażenia nigdy nie mają typu referencji, ale jako konwencja decltypereprezentują wyrażenia lvalue z referencjami lvalue).
Dlatego to narzędzie jest doskonałym narzędziem do nauki decltype, oprócz eksploracji i debugowania własnego kodu.
W przeciwieństwie do tego, gdybym to zbudował typeid(a).name(), bez dodawania utraconych kwalifikatorów cv lub referencji, wynik byłby następujący:
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
Tj. Każda referencja i kwalifikator cv są usuwane.
Aktualizacja C ++ 14
Właśnie wtedy, gdy myślisz, że masz rozwiązanie problemu, który został przybity, ktoś zawsze pojawia się znikąd i pokazuje ci znacznie lepszy sposób. :-)
Ta odpowiedź od Jamboree pokazuje jak uzyskać nazwę typu w C ++ 14 w czasie kompilacji. To genialne rozwiązanie z kilku powodów:
Jest w czasie kompilacji!
Dostajesz sam kompilator do wykonania zadania zamiast biblioteki (nawet std :: lib). Oznacza to dokładniejsze wyniki najnowszych funkcji językowych (takich jak lambdas).
Odpowiedź Jamboree nie wyjaśnia wszystkiego VS, a ja trochę poprawiam jego kod. Ale ponieważ ta odpowiedź zyskuje wiele wyświetleń, poświęć trochę czasu, aby tam głosować i głosować na jego odpowiedź, bez której aktualizacja nigdy by się nie wydarzyła.
Ten kod automatycznie się wycofuje, constexprjeśli nadal tkwisz w starożytnym C ++ 11. A jeśli malujesz na ścianie jaskini za pomocą C ++ 98/03, noexceptto również się poświęca.
Aktualizacja C ++ 17
W komentarzach poniżej Lyberta wskazuje, że nowy std::string_viewmoże zastąpić 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}
Zaktualizowałem stałe dla VS dzięki bardzo miłej pracy detektywistycznej Jive Dadson w komentarzach poniżej.
Aktualizacja:
Pamiętaj, aby sprawdzić to przepisać poniżej który eliminuje nieczytelne numery magiczne w moim najnowszym preparacie.
VS 14 CTP wydrukował prawidłowe typy, musiałem tylko dodać jedną #include <iostream>linię.
Max Galkin,
3
Dlaczego szablon <typename T> std :: string type_name ()? Dlaczego nie podajesz typu jako argumentu?
moonman239,
2
Wydaje mi się, że moim uzasadnieniem było to, że czasami miałem tylko typ (na przykład wydedukowany parametr szablonu) i nie chciałem sztucznie skonstruować jednego z nich, aby uzyskać ten typ (choć te dni declvalbyłyby wystarczające).
Howard Hinnant,
5
@AngelusMortis: Ponieważ angielski jest niejasny / dwuznaczny w porównaniu z kodem C ++, zachęcam do skopiowania / wklejenia go do testowego przypadku z typem, który Cię interesuje, i za pomocą konkretnego kompilatora, którym jesteś zainteresowany, i odpisania z większą ilością szczegóły, jeśli wynik jest zaskakujący i / lub niezadowalający.
Aby to zadziałało, może być konieczne aktywowanie RTTI w opcjach kompilatora. Dodatkowo wynik tego zależy od kompilatora. Może to być nazwa typu raw, symbol zmieniający nazwę lub cokolwiek pomiędzy.
Dlaczego ciąg zwracany przez funkcję name () jest zdefiniowany w implementacji?
Destructor
4
@PravasiMeet O ile wiem, nie ma dobrego powodu. Komitet po prostu nie chciał zmusić implementatorów kompilatora do określonych kierunków technicznych - prawdopodobnie z perspektywy czasu to błąd.
Konrad Rudolph,
2
Czy istnieje flaga, której mogłabym użyć, aby włączyć RTTI? Może udzielisz odpowiedzi.
Jim
4
@Destructor Podanie znormalizowanego formatu zniekształcania nazw może sprawiać wrażenie, że interoperacyjność między binariami zbudowanymi przez dwa różne kompilatory jest możliwa i / lub bezpieczna, jeśli nie jest. Ponieważ C ++ nie ma standardowego ABI, standardowy schemat zmiany nazw byłby bezcelowy, potencjalnie wprowadzający w błąd i niebezpieczny.
Elkvis
1
@Jim Sekcja na temat flag kompilatora byłaby o rząd wielkości dłuższa niż sama odpowiedź. GCC kompiluje się z nim domyślnie, stąd „-fno-rtti”, inne kompilatory mogą tego nie robić, ale nie ma standardu dla flag kompilatora.
kfsone
82
Bardzo brzydka, ale robi to, jeśli potrzebujesz tylko informacji o czasie kompilacji (np. Do debugowania):
auto testVar = std::make_tuple(1,1.0,"abc");decltype(testVar)::foo=1;
Zwroty:
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*>'
tylko c ++ może to utrudnić (drukowanie typu zmiennych automatycznych w czasie kompilacji). TYLKO C ++.
Karl Pickett,
3
@KarlP dobrze, żeby być uczciwym, to jest trochę skomplikowane, to też działa :) auto testVar = std::make_tuple(1, 1.0, "abc"); decltype(testVar)::foo = 1;
NickV
W wersji VC ++ 17 redukuje to odwołanie do wartości do zwykłego odwołania, nawet w funkcji szablonu z parametrem forwarding-referen, a nazwa obiektu jest zawinięta w std :: forward.
Jive Dadson,
Udało ci się dotrzeć do tego typu bez tworzenia nowych kół!
Steven Eckhoff
1
Technikę tę opisano również w „Pozycji 4: Wiedzieć, jak wyświetlać typy dedukcyjne” w Effective Modern C ++
lenkite,
54
Nie zapomnij dołączyć <typeinfo>
Uważam, że masz na myśli identyfikację typu środowiska wykonawczego. Możesz osiągnąć powyższe, robiąc.
Czy to nie będzie drukować „int” dla szortów i znaków? A „float” dla debla?
gartenriese
1
Specjalizacja @gartenriese nie ma tej wady. W doubletym celu skompilowałaby niespecjalistyczną wersję funkcji szablonu zamiast niejawnej konwersji typu w celu użycia specjalizacji: cpp.sh/2wzc
chappjc
1
@chappjc: Szczerze mówiąc, nie wiem, dlaczego o to prosiłem, teraz jest dla mnie całkiem jasne. Ale i tak dziękuję za odpowiedź na roczne pytanie!
gartenriese
2
@gartenriese Doszedłem do wniosku, ale „internet” może mieć to samo pytanie w pewnym momencie.
chappjc
18
Jak wspomniano, typeid().name()może zwrócić zniekształconą nazwę. W GCC (i niektórych innych kompilatorach) możesz obejść ten problem za pomocą następującego kodu:
#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;
The DECLARE_TYPE_NAMEDefine istnieje, aby ułatwić Ci życie, uznając tę klasę cech dla wszystkich typów można oczekiwać od potrzeb.
Może to być bardziej przydatne niż rozwiązania obejmujące, typeidponieważ można kontrolować dane wyjściowe. Na przykład użycie typeidfor long longw moim kompilatorze daje „x”.
W C ++ 11 mamy deklarację. W standardowym c ++ nie ma sposobu, aby wyświetlić dokładny typ zmiennej zadeklarowanej za pomocą decltype. Możemy użyć boost typeindex tj. type_id_with_cvr(Cvr oznacza const, volatile, referencja) do drukowania typu jak poniżej.
#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;}
czy byłoby prościej użyć funkcji pomocniczej:template<typename T> void print_type(T){cout << "type T is: "<< type_id_with_cvr<T>().pretty_name()<< '\n';}
r0ng 18.10.17
6
Możesz także użyć c ++ filt z opcją -t (typ), aby odformować nazwę typu:
Piekło brzydkie, ale zrobi to, czego potrzebuję. I znacznie mniejszy niż inne rozwiązania. Działa na komputerze Mac btw.
Marco Luglio,
6
Howard Hinnant użył magicznych liczb do wyodrębnienia nazwy typu. 瑋 桓 瑋 sugerował prefiks i sufiks łańcucha. Ale prefiks / sufiks ciągle się zmieniają. Z „typem sondy” nazwa_typu automatycznie oblicza rozmiary prefiksów i sufiksów dla „typu sondy”, aby wyodrębnić nazwę typu:
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]
Wersja 16.3.3 VS 2019:
class test
constint*&unsignedintconstintconstint*constint*&class std::basic_string_view<char,struct std::char_traits<char>> __cdecl type_name<class probe_type>(void)
Inne odpowiedzi dotyczące RTTI (typeid) są prawdopodobnie tym, czego potrzebujesz, o ile:
możesz sobie pozwolić na narzut pamięci (co może być znaczne w przypadku niektórych kompilatorów)
nazwy klas zwracane przez kompilator są przydatne
Alternatywą (podobną do odpowiedzi Grega Hewgilla) jest zbudowanie tabeli cech w czasie kompilacji.
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";};
Pamiętaj, że jeśli zawiniesz deklaracje w makrze, będziesz mieć problem z zadeklarowaniem nazw typów szablonów, które biorą więcej niż jeden parametr (np. Std :: map), z powodu przecinka.
Aby uzyskać dostęp do nazwy typu zmiennej, wystarczy
(i) nie będzie działać w przypadku innych typów (tj. nie jest ogólny); (ii) bezużyteczne wzdęcie kodu; (iii) to samo można (poprawnie) zrobić za pomocą typeidlub decltype.
edmz
2
Masz rację, ale obejmuje wszystkie podstawowe typy ... i właśnie tego teraz potrzebuję ..
Jahid
2
Czy możesz mi powiedzieć, jak zrobiłbyś to z dekliną,
Jahid
1
Jeśli jest to test czasu kompilacji, możesz użyć std :: is_same <T, S> i decltype, aby uzyskać T i S.
edmz
4
Kiedy rzucam wyzwanie, postanowiłem przetestować, jak daleko można się posunąć, korzystając z niezależnych od platformy (miejmy nadzieję) podstępów do szablonów.
Nazwy są kompletowane w momencie kompilacji. (Co znaczytypeid(T).name() że nie można użyć, dlatego należy jawnie podać nazwy dla typów nieskomplikowanych. W przeciwnym razie zostaną wyświetlone symbole zastępcze).
Przykładowe użycie:
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)`.}
Kod:
#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;}
Wynik:
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
Jak wyjaśnił Scott Meyers w Effective Modern C ++,
Zwraca się do std::type_info::namenie gwarantuje powrotu anythong sensowne.
Najlepszym rozwiązaniem jest umożliwienie kompilatorowi wygenerowania komunikatu o błędzie podczas odliczania typu, na przykład
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;}
Wynik będzie mniej więcej taki, w zależności od kompilatorów,
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;
Dlatego dowiadujemy się, że xtyp jest int, ytyp jestconst int*
Dla każdego, kto wciąż odwiedza, miałem ostatnio ten sam problem i postanowiłem napisać małą bibliotekę na podstawie odpowiedzi z tego postu. Zapewnia nazwy typów constexpr i indeksy typów oraz jest testowany na komputerach Mac, Windows i Ubuntu.
#define DEMANGLE_TYPEID_NAME(x) abi::__cxa_demangle(typeid((x)).name(), NULL, NULL, NULL)
. Jeśli potrzebujesz wsparcia cross-platform: Użyj#ifdef
,#else
,#endif
w celu dostarczenia jednego makra dla innych platform, takich jak MSVC.template<typename T> void print_T() { std::cout << __PRETTY_FUNCTION__ << '\n'; }
. Następnie użycie np.print_T<const int * const **>();
Wydrukujevoid print_T() [T = const int *const **]
w czasie wykonywania i zachowa wszystkie kwalifikatory (działa w GCC i Clang).__PRETTY_FUNCTION__
nie jest Standardowym C ++ (wymaganie jest w tytule pytania).Odpowiedzi:
Aktualizacja C ++ 11 do bardzo starego pytania: wypisz typ zmiennej w C ++.
Zaakceptowaną (i dobrą) odpowiedzią jest użycie
typeid(a).name()
, gdziea
jest nazwą zmiennej.Teraz w C ++ 11 mamy
decltype(x)
, który może zmienić wyrażenie w typ. Idecltype()
ma własny zestaw bardzo interesujących zasad. Na przykładdecltype(a)
i nadecltype((a))
ogół będą to różne typy (oraz z dobrych i zrozumiałych powodów po ujawnieniu tych powodów).Czy nasze zaufane rzeczy
typeid(a).name()
pomogą nam odkryć ten nowy wspaniały świat?Nie.
Ale narzędzie, które nie będzie tak skomplikowane. I to jest to narzędzie, którego używam jako odpowiedź na to pytanie. Porównuję i skontrastuję to nowe narzędzie
typeid(a).name()
. A to nowe narzędzie jest w rzeczywistości zbudowane na bazietypeid(a).name()
.Podstawowa kwestia:
wyrzuca kwalifikatory cv, referencje i lvalue / rvalue-ness. Na przykład:
Dla mnie wyniki:
i zgaduję na wyjściach MSVC:
Tj
const
. Zniknął. To nie jest kwestia QOI (Quality of Implementation). Norma nakazuje takie zachowanie.To co polecam poniżej to:
który byłby użyty w ten sposób:
a dla mnie wyniki:
<disclaimer>
Nie testowałem tego na MSVC.</disclaimer>
Ale cieszę się z opinii tych, którzy to robią.Rozwiązanie C ++ 11
Korzystam
__cxa_demangle
z platform innych niż MSVC, jak zalecił ipapadop w swojej odpowiedzi na typy trójkątów . Ale w MSVC ufam,typeid
że rozplątam nazwy (niesprawdzone). I ten rdzeń jest otoczony kilkoma prostymi testami, które wykrywają, przywracają i raportują kwalifikatory cv oraz odniesienia do typu danych wejściowych.Wyniki
Dzięki temu rozwiązaniu mogę to zrobić:
a wynikiem jest:
Uwaga (na przykład) różnica między
decltype(i)
idecltype((i))
. Były to rodzaj deklaracji oi
. Ten ostatni jest „typem” wyrażeniai
. (wyrażenia nigdy nie mają typu referencji, ale jako konwencjadecltype
reprezentują wyrażenia lvalue z referencjami lvalue).Dlatego to narzędzie jest doskonałym narzędziem do nauki
decltype
, oprócz eksploracji i debugowania własnego kodu.W przeciwieństwie do tego, gdybym to zbudował
typeid(a).name()
, bez dodawania utraconych kwalifikatorów cv lub referencji, wynik byłby następujący:Tj. Każda referencja i kwalifikator cv są usuwane.
Aktualizacja C ++ 14
Właśnie wtedy, gdy myślisz, że masz rozwiązanie problemu, który został przybity, ktoś zawsze pojawia się znikąd i pokazuje ci znacznie lepszy sposób. :-)
Ta odpowiedź od Jamboree pokazuje jak uzyskać nazwę typu w C ++ 14 w czasie kompilacji. To genialne rozwiązanie z kilku powodów:
Odpowiedź Jamboree nie wyjaśnia wszystkiego VS, a ja trochę poprawiam jego kod. Ale ponieważ ta odpowiedź zyskuje wiele wyświetleń, poświęć trochę czasu, aby tam głosować i głosować na jego odpowiedź, bez której aktualizacja nigdy by się nie wydarzyła.
Ten kod automatycznie się wycofuje,
constexpr
jeśli nadal tkwisz w starożytnym C ++ 11. A jeśli malujesz na ścianie jaskini za pomocą C ++ 98/03,noexcept
to również się poświęca.Aktualizacja C ++ 17
W komentarzach poniżej Lyberta wskazuje, że nowy
std::string_view
może zastąpićstatic_string
:Zaktualizowałem stałe dla VS dzięki bardzo miłej pracy detektywistycznej Jive Dadson w komentarzach poniżej.
Aktualizacja:
Pamiętaj, aby sprawdzić to przepisać poniżej który eliminuje nieczytelne numery magiczne w moim najnowszym preparacie.
źródło
#include <iostream>
linię.declval
byłyby wystarczające).std::string_view
zamiaststatic_string
?Próbować:
Aby to zadziałało, może być konieczne aktywowanie RTTI w opcjach kompilatora. Dodatkowo wynik tego zależy od kompilatora. Może to być nazwa typu raw, symbol zmieniający nazwę lub cokolwiek pomiędzy.
źródło
Bardzo brzydka, ale robi to, jeśli potrzebujesz tylko informacji o czasie kompilacji (np. Do debugowania):
Zwroty:
źródło
auto testVar = std::make_tuple(1, 1.0, "abc"); decltype(testVar)::foo = 1;
Nie zapomnij dołączyć
<typeinfo>
Uważam, że masz na myśli identyfikację typu środowiska wykonawczego. Możesz osiągnąć powyższe, robiąc.
źródło
Według rozwiązania Howarda , jeśli nie chcesz magicznej liczby, myślę, że to dobry sposób na przedstawienie i wygląda intuicyjnie:
źródło
Należy pamiętać, że nazwy generowane przez funkcję RTTI w C ++ nie są przenośne. Na przykład klasa
będzie miał następujące nazwy:
Nie możesz więc użyć tych informacji do serializacji. Ale nadal właściwość typeid (a) .name () może być nadal używana do celów dziennika / debugowania
źródło
Możesz użyć szablonów.
W powyższym przykładzie, gdy typ nie jest dopasowany, wypisze „nieznany”.
źródło
double
tym celu skompilowałaby niespecjalistyczną wersję funkcji szablonu zamiast niejawnej konwersji typu w celu użycia specjalizacji: cpp.sh/2wzcJak wspomniano,
typeid().name()
może zwrócić zniekształconą nazwę. W GCC (i niektórych innych kompilatorach) możesz obejść ten problem za pomocą następującego kodu:}
źródło
Możesz do tego użyć klasy cech. Coś jak:
The
DECLARE_TYPE_NAME
Define istnieje, aby ułatwić Ci życie, uznając tę klasę cech dla wszystkich typów można oczekiwać od potrzeb.Może to być bardziej przydatne niż rozwiązania obejmujące,
typeid
ponieważ można kontrolować dane wyjściowe. Na przykład użycietypeid
forlong long
w moim kompilatorze daje „x”.źródło
W C ++ 11 mamy deklarację. W standardowym c ++ nie ma sposobu, aby wyświetlić dokładny typ zmiennej zadeklarowanej za pomocą decltype. Możemy użyć boost typeindex tj.
type_id_with_cvr
(Cvr oznacza const, volatile, referencja) do drukowania typu jak poniżej.źródło
template<typename T> void print_type(T){cout << "type T is: "<< type_id_with_cvr<T>().pretty_name()<< '\n';}
Możesz także użyć c ++ filt z opcją -t (typ), aby odformować nazwę typu:
Testowane tylko na systemie Linux.
źródło
Howard Hinnant użył magicznych liczb do wyodrębnienia nazwy typu. 瑋 桓 瑋 sugerował prefiks i sufiks łańcucha. Ale prefiks / sufiks ciągle się zmieniają. Z „typem sondy” nazwa_typu automatycznie oblicza rozmiary prefiksów i sufiksów dla „typu sondy”, aby wyodrębnić nazwę typu:
Wynik
gcc 10.0.0 20190919 Wandbox:
clang 10.0.0 Wandbox:
Wersja 16.3.3 VS 2019:
źródło
Inne odpowiedzi dotyczące RTTI (typeid) są prawdopodobnie tym, czego potrzebujesz, o ile:
Alternatywą (podobną do odpowiedzi Grega Hewgilla) jest zbudowanie tabeli cech w czasie kompilacji.
Pamiętaj, że jeśli zawiniesz deklaracje w makrze, będziesz mieć problem z zadeklarowaniem nazw typów szablonów, które biorą więcej niż jeden parametr (np. Std :: map), z powodu przecinka.
Aby uzyskać dostęp do nazwy typu zmiennej, wystarczy
źródło
Bardziej ogólne rozwiązanie bez przeciążania funkcji niż moje poprzednie:
Tutaj MyClass jest klasą zdefiniowaną przez użytkownika. Można również dodać więcej warunków.
Przykład:
Wynik:
źródło
Podoba mi się metoda Nicka, może to być kompletny formularz (dla wszystkich podstawowych typów danych):
źródło
typeid
lubdecltype
.Kiedy rzucam wyzwanie, postanowiłem przetestować, jak daleko można się posunąć, korzystając z niezależnych od platformy (miejmy nadzieję) podstępów do szablonów.
Nazwy są kompletowane w momencie kompilacji. (Co znaczy
typeid(T).name()
że nie można użyć, dlatego należy jawnie podać nazwy dla typów nieskomplikowanych. W przeciwnym razie zostaną wyświetlone symbole zastępcze).Przykładowe użycie:
Kod:
źródło
Wynik:
źródło
Jak wyjaśnił Scott Meyers w Effective Modern C ++,
Najlepszym rozwiązaniem jest umożliwienie kompilatorowi wygenerowania komunikatu o błędzie podczas odliczania typu, na przykład
Wynik będzie mniej więcej taki, w zależności od kompilatorów,
Dlatego dowiadujemy się, że
x
typ jestint
,y
typ jestconst int*
źródło
Dla każdego, kto wciąż odwiedza, miałem ostatnio ten sam problem i postanowiłem napisać małą bibliotekę na podstawie odpowiedzi z tego postu. Zapewnia nazwy typów constexpr i indeksy typów oraz jest testowany na komputerach Mac, Windows i Ubuntu.
Kod biblioteki znajduje się tutaj: https://github.com/TheLartians/StaticTypeInfo
źródło