Berikut adalah ringkasan dari solusi bentuk panjang Howard tapi diimplementasikan dengan satu baris makro sesat: #define DEMANGLE_TYPEID_NAME(x) abi::__cxa_demangle(typeid((x)).name(), NULL, NULL, NULL). Jika anda memerlukan dukungan cross-platform: Gunakan #ifdef, #else, #endifuntuk menyediakan satu macro untuk platform lain seperti MSVC.
Jika Anda hanya menggunakan ini untuk debugging, Anda mungkin ingin mempertimbangkan template<typename T> void print_T() { std::cout << __PRETTY_FUNCTION__ << '\n'; }. Kemudian menggunakan eg print_T<const int * const **>();akan mencetak void print_T() [T = const int *const **]pada saat runtime dan mempertahankan semua kualifikasi (berfungsi di GCC dan Dentang).
Henri Menke
@ Henri, __PRETTY_FUNCTION__bukan Standar C ++ (persyaratan ada di judul pertanyaan).
Toby Speight
Jawaban:
505
Pembaruan C ++ 11 ke pertanyaan yang sangat lama: Cetak tipe variabel dalam C ++.
Jawaban yang diterima (dan baik) adalah menggunakan typeid(a).name(), di mana aada nama variabel.
Sekarang di C ++ 11 yang kita miliki decltype(x), yang dapat mengubah ekspresi menjadi tipe. Dan decltype()hadir dengan seperangkat aturan yang sangat menarik. Sebagai contoh decltype(a)dan decltype((a))umumnya akan menjadi jenis yang berbeda (dan untuk alasan yang baik dan dapat dimengerti begitu alasan tersebut diungkapkan).
Apakah kepercayaan kita akan typeid(a).name()membantu kita menjelajahi dunia baru yang berani ini?
Tidak.
Tetapi alat yang akan tidak rumit. Dan itu adalah alat yang saya gunakan sebagai jawaban untuk pertanyaan ini. Saya akan membandingkan dan membandingkan alat baru ini dengan typeid(a).name(). Dan alat baru ini sebenarnya dibangun di atas typeid(a).name().
Masalah mendasar:
typeid(a).name()
membuang kualifikasi-cv, referensi, dan nilai / nilai-nilai. Sebagai contoh:
constint ci =0;
std::cout <<typeid(ci).name()<<'\n';
Bagi saya output:
i
dan saya menebak pada output MSVC:
int
Yaitu consthilang. Ini bukan masalah QOI (Kualitas Implementasi). Standar mengamanatkan perilaku ini.
Yang saya rekomendasikan di bawah ini adalah:
template<typename T> std::string type_name();
yang akan digunakan seperti ini:
constint ci =0;
std::cout << type_name<decltype(ci)>()<<'\n';
dan untuk saya output:
intconst
<disclaimer>Saya belum menguji ini di MSVC. </disclaimer> Tapi saya menyambut umpan balik dari mereka yang melakukannya.
Solusi C ++ 11
Saya menggunakan __cxa_demangleuntuk platform non-MSVC seperti yang direkomendasikan oleh ipapadop dalam jawabannya untuk jenis demangle. Tetapi pada MSVC saya percaya typeiduntuk merendahkan nama (belum diuji). Dan inti ini melilit beberapa pengujian sederhana yang mendeteksi, mengembalikan, dan melaporkan kualifikasi-cv dan referensi ke tipe input.
#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;}
Hasil
Dengan solusi ini saya bisa melakukan ini:
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';}
dan hasilnya adalah:
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
Perhatikan (misalnya) perbedaan antara decltype(i)dan decltype((i)). Yang pertama adalah jenis dari deklarasi dari i. Yang terakhir adalah "tipe" dari ekspresii . (Ekspresi tidak pernah memiliki tipe referensi, tetapi sebagai konvensi decltypemewakili ekspresi lvalue dengan referensi lvalue).
Dengan demikian alat ini adalah kendaraan yang sangat baik hanya untuk belajar tentang decltype, selain menjelajahi dan men-debug kode Anda sendiri.
Sebaliknya, jika saya membangun ini hanya di atas typeid(a).name(), tanpa menambahkan kembali cv-kualifikasi atau referensi yang hilang, hasilnya adalah:
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
Yaitu Setiap referensi dan kualifikasi cv dilucuti.
Pembaruan C ++ 14
Hanya ketika Anda berpikir Anda punya solusi untuk masalah yang dipaku, seseorang selalu datang entah dari mana dan menunjukkan cara yang jauh lebih baik. :-)
Jawaban dari Jambore ini menunjukkan cara mendapatkan nama tipe dalam C ++ 14 pada waktu kompilasi. Ini adalah solusi cemerlang untuk beberapa alasan:
Ini pada waktu kompilasi!
Anda mendapatkan kompiler itu sendiri untuk melakukan pekerjaan alih-alih perpustakaan (bahkan std :: lib). Ini berarti hasil yang lebih akurat untuk fitur bahasa terbaru (seperti lambdas).
Jawaban Jambore tidak menjelaskan segalanya untuk VS, dan saya sedikit mengubah kodenya. Tetapi karena jawaban ini mendapat banyak pandangan, luangkan waktu untuk pergi ke sana dan membarui jawabannya, yang tanpanya, pembaruan ini tidak akan pernah terjadi.
Kode ini akan secara otomatis mundur pada constexpr jika Anda masih terjebak di C ++ 11 kuno. Dan jika Anda melukis di dinding gua dengan C ++ 98/03, noexceptdikorbankan juga.
Pembaruan C ++ 17
Dalam komentar di bawah, Lyberta menunjukkan bahwa yang baru std::string_viewdapat menggantikan 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}
Saya telah memperbarui konstanta untuk VS berkat karya detektif yang sangat bagus oleh Jive Dadson dalam komentar di bawah.
Memperbarui:
Pastikan untuk memeriksa kembali penulisan ulang di bawah ini yang menghilangkan angka ajaib yang tidak dapat dibaca dalam formulasi terbaru saya.
VS 14 CTP dicetak jenis yang benar, saya hanya perlu menambahkan satu #include <iostream>baris.
Max Galkin
3
Mengapa templat <typename T> std :: string type_name ()? Mengapa Anda tidak mengirimkan tipe sebagai argumen?
moonman239
2
Saya percaya alasan saya adalah bahwa kadang-kadang saya hanya memiliki tipe (seperti parameter template yang disimpulkan), dan saya tidak ingin harus secara artifisial membangun salah satu dari mereka untuk mendapatkan tipe (meskipun hari ini declvalakan melakukan pekerjaan).
Howard Hinnant
5
@AngelusMortis: Karena bahasa Inggris tidak jelas / ambigu dibandingkan dengan kode C ++, saya mendorong Anda untuk menyalin / menempelkan ini ke dalam test case Anda dengan tipe spesifik yang Anda minati, dan dengan kompiler spesifik yang Anda minati, dan menulis kembali dengan lebih banyak perincian jika hasilnya mengejutkan dan / atau tidak memuaskan.
Howard Hinnant
3
@HowardHinnant dapat Anda gunakan std::string_viewbukan static_string?
Anda mungkin harus mengaktifkan RTTI dalam opsi kompiler Anda agar ini berfungsi. Selain itu, output ini tergantung pada kompiler. Ini mungkin nama jenis mentah atau simbol nama mangling atau apa pun di antaranya.
Mengapa fungsi string dikembalikan dengan nama () didefinisikan?
Destructor
4
@PravasiMeet Tidak ada alasan bagus, sejauh yang saya tahu. Komite hanya tidak ingin memaksa implementor kompiler ke arah teknis tertentu - mungkin kesalahan, di belakang.
Konrad Rudolph
2
Apakah ada bendera yang bisa saya gunakan untuk mengaktifkan RTTI? Mungkin Anda bisa membuat jawaban Anda inklusif.
Jim
4
@Destructor Menyediakan format standar nama mangling mungkin memberi kesan bahwa interoperabilitas antara binari yang dibangun oleh dua kompiler yang berbeda adalah mungkin dan / atau aman, jika tidak. Karena C ++ tidak memiliki ABI standar, skema mangling nama standar akan sia-sia, dan berpotensi menyesatkan dan berbahaya.
Elkvis
1
@ Jim Bagian pada flag compiler akan menjadi urutan besarnya lebih lama dari jawaban itu sendiri. GCC mengkompilasinya secara default, karenanya "-fno-rtti", kompiler lain mungkin memilih untuk tidak melakukannya, tetapi tidak ada standar untuk flag kompiler.
kfsone
82
Sangat buruk tetapi melakukan trik jika Anda hanya ingin mengkompilasi info waktu (misalnya untuk debugging):
auto testVar = std::make_tuple(1,1.0,"abc");decltype(testVar)::foo=1;
Pengembalian:
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*>'
hanya c ++ yang dapat membuat ini sangat sulit (mencetak tipe variabel otomatis pada waktu kompilasi). HANYA C ++.
Karl Pickett
3
@ KarlP harus adil itu agak berbelit-belit, ini juga bekerja :) auto testVar = std::make_tuple(1, 1.0, "abc"); decltype(testVar)::foo = 1;
NickV
Pada VC ++ 17, ini mengurangi referensi nilai ke referensi polos, bahkan dalam fungsi templat dengan parameter referensi penerusan, dan nama objek yang dibungkus dengan std :: forward.
Jive Dadson
Anda bisa mendapatkan tipe tanpa membuat roda baru!
Steven Eckhoff
1
Teknik ini juga dijelaskan dalam "Butir 4: Tahu cara melihat tipe yang disimpulkan" dalam C Modern Modern ++
lenkite
54
Jangan lupa untuk memasukkan <typeinfo>
Saya percaya apa yang Anda maksud adalah identifikasi tipe runtime. Anda dapat mencapai hal di atas dengan melakukan.
Jadi, Anda tidak dapat menggunakan informasi ini untuk serialisasi. Tapi tetap saja, properti typeid (a) .name () masih bisa digunakan untuk keperluan log / debug
Bukankah itu mencetak "int" untuk celana pendek dan karakter? Dan "mengambang" untuk ganda?
gartenriese
1
@gartenriese Spesialisasi tidak memiliki kekurangan itu. Untuk doubleitu akan mengkompilasi versi non-khusus fungsi template daripada melakukan konversi tipe implisit untuk menggunakan spesialisasi: cpp.sh/2wzc
chappjc
1
@ chappjc: Sejujurnya saya tidak tahu mengapa saya menanyakan hal itu, cukup jelas bagi saya sekarang. Tapi terima kasih sudah menjawab pertanyaan berumur setahun!
gartenriese
2
@ gartenriese Saya pikir banyak, tapi "internet" mungkin memiliki pertanyaan yang sama di beberapa titik.
chappjc
18
Seperti disebutkan, typeid().name()dapat mengembalikan nama yang rusak. Di GCC (dan beberapa kompiler lain) Anda dapat mengatasinya dengan kode berikut:
#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;
Itu DECLARE_TYPE_NAME define ada untuk membuat hidup Anda lebih mudah dalam mendeklarasikan kelas ciri ini untuk semua jenis yang Anda butuhkan.
Ini mungkin lebih bermanfaat daripada solusi yang terlibat typeidkarena Anda bisa mengendalikan output. Sebagai contoh, menggunakan typeiduntuk long longpada kompiler saya memberi "x".
Dalam C ++ 11, kami memiliki jenis mendeklarasikan. Tidak ada cara dalam standar c ++ untuk menampilkan tipe variabel tepat yang dideklarasikan menggunakan decltype. Kita dapat menggunakan boost typeindex yaitu type_id_with_cvr(cvr adalah singkatan dari const, volatile, reference) untuk mencetak tipe seperti di bawah ini.
#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;}
apakah lebih sederhana menggunakan fungsi pembantu:template<typename T> void print_type(T){cout << "type T is: "<< type_id_with_cvr<T>().pretty_name()<< '\n';}
r0ng
6
Anda juga dapat menggunakan c ++ filt dengan opsi -t (type) untuk menghapus nama tipe:
Neraka jelek tapi akan melakukan apa yang aku butuhkan. Dan jauh lebih kecil dari solusi lainnya. Bekerja di Mac btw.
Marco Luglio
6
Howard Hinnant menggunakan angka ajaib untuk mengekstrak nama tipe. 康 桓 瑋 menyarankan awalan dan akhiran string. Namun awalan / akhiran terus berubah. Dengan "probe_type" type_name secara otomatis menghitung ukuran awalan dan akhiran untuk "probe_type" untuk mengekstrak nama tipe:
test
constint*&unsignedintconstintconstint*constint*&constexpr std::string_view type_name()[with T = probe_type; std::string_view = std::basic_string_view<char>]
dentang 10.0.0 Wandbox:
test
constint*&unsignedintconstintconstint*constint*&
std::__1::string_view type_name()[T = probe_type]
VS 2019 versi 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)
Jawaban lain yang melibatkan RTTI (tipeid) mungkin yang Anda inginkan, selama:
Anda dapat membeli overhead memori (yang dapat dipertimbangkan dengan beberapa kompiler)
nama kelas yang dikembalikan kompiler Anda bermanfaat
Alternatifnya, (mirip dengan jawaban Greg Hewgill), adalah membangun tabel waktu kompilasi sifat.
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";};
Perlu diketahui bahwa jika Anda membungkus deklarasi dalam makro, Anda akan mengalami kesulitan dalam mendeklarasikan nama untuk tipe templat yang mengambil lebih dari satu parameter (mis. Std :: map), karena koma.
Untuk mengakses nama tipe variabel, yang Anda butuhkan adalah
(i) itu tidak akan bekerja untuk jenis lain (yaitu tidak generik sama sekali); (ii) mengasapi kode yang tidak berguna; (iii) hal yang sama dapat dilakukan (dengan benar) dengan typeidatau decltype.
edmz
2
Anda benar, tetapi itu mencakup semua tipe dasar ... dan itulah yang saya butuhkan saat ini ..
Jahid
2
Dapatkah Anda memberi tahu saya, bagaimana Anda akan melakukannya dengan decltype,
Jahid
1
Jika ini adalah tes kompilasi-waktu, Anda dapat menggunakan std :: is_same <T, S> dan decltype untuk mendapatkan T dan S.
edmz
4
Saat saya menantang, saya memutuskan untuk menguji seberapa jauh seseorang bisa menggunakan tipuan template platform-independent (semoga).
Nama-nama dirakit sepenuhnya pada waktu kompilasi. (Yang berartitypeid(T).name() tidak dapat digunakan, sehingga Anda harus secara eksplisit memberikan nama untuk tipe non-majemuk. Jika tidak, placeholder akan ditampilkan sebagai gantinya.)
Contoh penggunaan:
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)`.}
Kode:
#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;}
Keluaran:
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
Seperti yang dijelaskan oleh Scott Meyers dalam Effective Modern C ++,
Panggilan untuk std::type_info::nametidak dijamin untuk mengembalikan apa pun yang masuk akal.
Solusi terbaik adalah membiarkan kompiler membuat pesan kesalahan selama pengurangan tipe, misalnya,
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;}
Hasilnya akan menjadi seperti ini, tergantung pada kompiler,
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;
Karenanya, kita mengetahui bahwa xtipe itu adalah int, ytipe ituconst int*
Bagi siapa pun yang masih berkunjung, saya baru saja mengalami masalah yang sama dan memutuskan untuk menulis perpustakaan kecil berdasarkan jawaban dari posting ini. Ini memberikan nama tipe constexpr dan indeks tipe und diuji pada Mac, Windows dan Ubuntu.
#define DEMANGLE_TYPEID_NAME(x) abi::__cxa_demangle(typeid((x)).name(), NULL, NULL, NULL)
. Jika anda memerlukan dukungan cross-platform: Gunakan#ifdef
,#else
,#endif
untuk menyediakan satu macro untuk platform lain seperti MSVC.template<typename T> void print_T() { std::cout << __PRETTY_FUNCTION__ << '\n'; }
. Kemudian menggunakan egprint_T<const int * const **>();
akan mencetakvoid print_T() [T = const int *const **]
pada saat runtime dan mempertahankan semua kualifikasi (berfungsi di GCC dan Dentang).__PRETTY_FUNCTION__
bukan Standar C ++ (persyaratan ada di judul pertanyaan).Jawaban:
Pembaruan C ++ 11 ke pertanyaan yang sangat lama: Cetak tipe variabel dalam C ++.
Jawaban yang diterima (dan baik) adalah menggunakan
typeid(a).name()
, di manaa
ada nama variabel.Sekarang di C ++ 11 yang kita miliki
decltype(x)
, yang dapat mengubah ekspresi menjadi tipe. Dandecltype()
hadir dengan seperangkat aturan yang sangat menarik. Sebagai contohdecltype(a)
dandecltype((a))
umumnya akan menjadi jenis yang berbeda (dan untuk alasan yang baik dan dapat dimengerti begitu alasan tersebut diungkapkan).Apakah kepercayaan kita akan
typeid(a).name()
membantu kita menjelajahi dunia baru yang berani ini?Tidak.
Tetapi alat yang akan tidak rumit. Dan itu adalah alat yang saya gunakan sebagai jawaban untuk pertanyaan ini. Saya akan membandingkan dan membandingkan alat baru ini dengan
typeid(a).name()
. Dan alat baru ini sebenarnya dibangun di atastypeid(a).name()
.Masalah mendasar:
membuang kualifikasi-cv, referensi, dan nilai / nilai-nilai. Sebagai contoh:
Bagi saya output:
dan saya menebak pada output MSVC:
Yaitu
const
hilang. Ini bukan masalah QOI (Kualitas Implementasi). Standar mengamanatkan perilaku ini.Yang saya rekomendasikan di bawah ini adalah:
yang akan digunakan seperti ini:
dan untuk saya output:
<disclaimer>
Saya belum menguji ini di MSVC.</disclaimer>
Tapi saya menyambut umpan balik dari mereka yang melakukannya.Solusi C ++ 11
Saya menggunakan
__cxa_demangle
untuk platform non-MSVC seperti yang direkomendasikan oleh ipapadop dalam jawabannya untuk jenis demangle. Tetapi pada MSVC saya percayatypeid
untuk merendahkan nama (belum diuji). Dan inti ini melilit beberapa pengujian sederhana yang mendeteksi, mengembalikan, dan melaporkan kualifikasi-cv dan referensi ke tipe input.Hasil
Dengan solusi ini saya bisa melakukan ini:
dan hasilnya adalah:
Perhatikan (misalnya) perbedaan antara
decltype(i)
dandecltype((i))
. Yang pertama adalah jenis dari deklarasi darii
. Yang terakhir adalah "tipe" dari ekspresii
. (Ekspresi tidak pernah memiliki tipe referensi, tetapi sebagai konvensidecltype
mewakili ekspresi lvalue dengan referensi lvalue).Dengan demikian alat ini adalah kendaraan yang sangat baik hanya untuk belajar tentang
decltype
, selain menjelajahi dan men-debug kode Anda sendiri.Sebaliknya, jika saya membangun ini hanya di atas
typeid(a).name()
, tanpa menambahkan kembali cv-kualifikasi atau referensi yang hilang, hasilnya adalah:Yaitu Setiap referensi dan kualifikasi cv dilucuti.
Pembaruan C ++ 14
Hanya ketika Anda berpikir Anda punya solusi untuk masalah yang dipaku, seseorang selalu datang entah dari mana dan menunjukkan cara yang jauh lebih baik. :-)
Jawaban dari Jambore ini menunjukkan cara mendapatkan nama tipe dalam C ++ 14 pada waktu kompilasi. Ini adalah solusi cemerlang untuk beberapa alasan:
Jawaban Jambore tidak menjelaskan segalanya untuk VS, dan saya sedikit mengubah kodenya. Tetapi karena jawaban ini mendapat banyak pandangan, luangkan waktu untuk pergi ke sana dan membarui jawabannya, yang tanpanya, pembaruan ini tidak akan pernah terjadi.
Kode ini akan secara otomatis mundur pada
constexpr
jika Anda masih terjebak di C ++ 11 kuno. Dan jika Anda melukis di dinding gua dengan C ++ 98/03,noexcept
dikorbankan juga.Pembaruan C ++ 17
Dalam komentar di bawah, Lyberta menunjukkan bahwa yang baru
std::string_view
dapat menggantikanstatic_string
:Saya telah memperbarui konstanta untuk VS berkat karya detektif yang sangat bagus oleh Jive Dadson dalam komentar di bawah.
Memperbarui:
Pastikan untuk memeriksa kembali penulisan ulang di bawah ini yang menghilangkan angka ajaib yang tidak dapat dibaca dalam formulasi terbaru saya.
sumber
#include <iostream>
baris.declval
akan melakukan pekerjaan).std::string_view
bukanstatic_string
?Mencoba:
Anda mungkin harus mengaktifkan RTTI dalam opsi kompiler Anda agar ini berfungsi. Selain itu, output ini tergantung pada kompiler. Ini mungkin nama jenis mentah atau simbol nama mangling atau apa pun di antaranya.
sumber
Sangat buruk tetapi melakukan trik jika Anda hanya ingin mengkompilasi info waktu (misalnya untuk debugging):
Pengembalian:
sumber
auto testVar = std::make_tuple(1, 1.0, "abc"); decltype(testVar)::foo = 1;
Jangan lupa untuk memasukkan
<typeinfo>
Saya percaya apa yang Anda maksud adalah identifikasi tipe runtime. Anda dapat mencapai hal di atas dengan melakukan.
sumber
Menurut solusi Howard , jika Anda tidak menginginkan angka ajaib, saya pikir ini adalah cara yang baik untuk mewakili dan terlihat intuitif:
sumber
Perhatikan bahwa nama-nama yang dihasilkan oleh fitur RTTI dari C ++ tidak portabel. Misalnya kelas
akan memiliki nama-nama berikut:
Jadi, Anda tidak dapat menggunakan informasi ini untuk serialisasi. Tapi tetap saja, properti typeid (a) .name () masih bisa digunakan untuk keperluan log / debug
sumber
Anda dapat menggunakan templat.
Pada contoh di atas, ketika jenis tidak cocok itu akan mencetak "tidak dikenal".
sumber
double
itu akan mengkompilasi versi non-khusus fungsi template daripada melakukan konversi tipe implisit untuk menggunakan spesialisasi: cpp.sh/2wzcSeperti disebutkan,
typeid().name()
dapat mengembalikan nama yang rusak. Di GCC (dan beberapa kompiler lain) Anda dapat mengatasinya dengan kode berikut:}
sumber
Anda bisa menggunakan kelas ciri untuk ini. Sesuatu seperti:
Itu
DECLARE_TYPE_NAME
define ada untuk membuat hidup Anda lebih mudah dalam mendeklarasikan kelas ciri ini untuk semua jenis yang Anda butuhkan.Ini mungkin lebih bermanfaat daripada solusi yang terlibat
typeid
karena Anda bisa mengendalikan output. Sebagai contoh, menggunakantypeid
untuklong long
pada kompiler saya memberi "x".sumber
Dalam C ++ 11, kami memiliki jenis mendeklarasikan. Tidak ada cara dalam standar c ++ untuk menampilkan tipe variabel tepat yang dideklarasikan menggunakan decltype. Kita dapat menggunakan boost typeindex yaitu
type_id_with_cvr
(cvr adalah singkatan dari const, volatile, reference) untuk mencetak tipe seperti di bawah ini.sumber
template<typename T> void print_type(T){cout << "type T is: "<< type_id_with_cvr<T>().pretty_name()<< '\n';}
Anda juga dapat menggunakan c ++ filt dengan opsi -t (type) untuk menghapus nama tipe:
Diuji hanya di linux.
sumber
Howard Hinnant menggunakan angka ajaib untuk mengekstrak nama tipe. 康 桓 瑋 menyarankan awalan dan akhiran string. Namun awalan / akhiran terus berubah. Dengan "probe_type" type_name secara otomatis menghitung ukuran awalan dan akhiran untuk "probe_type" untuk mengekstrak nama tipe:
Keluaran
gcc 10.0.0 20190919 Wandbox:
dentang 10.0.0 Wandbox:
VS 2019 versi 16.3.3:
sumber
Jawaban lain yang melibatkan RTTI (tipeid) mungkin yang Anda inginkan, selama:
Alternatifnya, (mirip dengan jawaban Greg Hewgill), adalah membangun tabel waktu kompilasi sifat.
Perlu diketahui bahwa jika Anda membungkus deklarasi dalam makro, Anda akan mengalami kesulitan dalam mendeklarasikan nama untuk tipe templat yang mengambil lebih dari satu parameter (mis. Std :: map), karena koma.
Untuk mengakses nama tipe variabel, yang Anda butuhkan adalah
sumber
Solusi yang lebih umum tanpa kelebihan fungsi dari yang sebelumnya:
Di sini MyClass adalah kelas yang ditentukan pengguna. Lebih banyak kondisi dapat ditambahkan di sini juga.
Contoh:
Keluaran:
sumber
Saya suka metode Nick, Bentuk lengkap mungkin ini (untuk semua tipe data dasar):
sumber
typeid
ataudecltype
.Saat saya menantang, saya memutuskan untuk menguji seberapa jauh seseorang bisa menggunakan tipuan template platform-independent (semoga).
Nama-nama dirakit sepenuhnya pada waktu kompilasi. (Yang berarti
typeid(T).name()
tidak dapat digunakan, sehingga Anda harus secara eksplisit memberikan nama untuk tipe non-majemuk. Jika tidak, placeholder akan ditampilkan sebagai gantinya.)Contoh penggunaan:
Kode:
sumber
Keluaran:
sumber
Seperti yang dijelaskan oleh Scott Meyers dalam Effective Modern C ++,
Solusi terbaik adalah membiarkan kompiler membuat pesan kesalahan selama pengurangan tipe, misalnya,
Hasilnya akan menjadi seperti ini, tergantung pada kompiler,
Karenanya, kita mengetahui bahwa
x
tipe itu adalahint
,y
tipe ituconst int*
sumber
Bagi siapa pun yang masih berkunjung, saya baru saja mengalami masalah yang sama dan memutuskan untuk menulis perpustakaan kecil berdasarkan jawaban dari posting ini. Ini memberikan nama tipe constexpr dan indeks tipe und diuji pada Mac, Windows dan Ubuntu.
Kode perpustakaan ada di sini: https://github.com/TheLartians/StaticTypeInfo
sumber