Apakah mungkin untuk mencetak jenis variabel dalam standar C ++?

393

Sebagai contoh:

int a = 12;
cout << typeof(a) << endl;

Output yang diharapkan:

int
Jorge Ferreira
sumber
2
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.
Trevor Boyd Smith
Dengan persyaratan yang dapat dibaca manusia yang lebih eksplisit: stackoverflow.com/questions/12877521/…
Ciro Santilli 郝海东 冠状 病 六四 事件 事件
3
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:

const int 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:

const int ci = 0;
std::cout << type_name<decltype(ci)>() << '\n';

dan untuk saya output:

int const

<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()
{
    typedef typename 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),
#else
                nullptr,
#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 += "&";
    else if (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;
    const int 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 int
decltype((i)) is int&
decltype(ci) is int const
decltype((ci)) is int const&
decltype(static_cast<int&>(i)) is int&
decltype(static_cast<int&&>(i)) is int&&
decltype(static_cast<int>(i)) is int
decltype(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 ekspresi i . (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 int
decltype((i)) is int
decltype(ci) is int
decltype((ci)) is int
decltype(static_cast<int&>(i)) is int
decltype(static_cast<int&&>(i)) is int
decltype(static_cast<int>(i)) is int
decltype(foo_lref()) is int
decltype(foo_rref()) is int
decltype(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:

  1. Ini pada waktu kompilasi!
  2. 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.

#include <cstddef>
#include <stdexcept>
#include <cstring>
#include <ostream>

#ifndef _MSC_VER
#  if __cplusplus < 201103
#    define CONSTEXPR11_TN
#    define CONSTEXPR14_TN
#    define NOEXCEPT_TN
#  elif __cplusplus < 201402
#    define CONSTEXPR11_TN constexpr
#    define CONSTEXPR14_TN
#    define NOEXCEPT_TN noexcept
#  else
#    define CONSTEXPR11_TN constexpr
#    define CONSTEXPR14_TN constexpr
#    define NOEXCEPT_TN noexcept
#  endif
#else  // _MSC_VER
#  if _MSC_VER < 1900
#    define CONSTEXPR11_TN
#    define CONSTEXPR14_TN
#    define NOEXCEPT_TN
#  elif _MSC_VER < 2000
#    define CONSTEXPR11_TN constexpr
#    define CONSTEXPR14_TN
#    define NOEXCEPT_TN noexcept
#  else
#    define CONSTEXPR11_TN constexpr
#    define CONSTEXPR14_TN constexpr
#    define NOEXCEPT_TN noexcept
#  endif
#endif  // _MSC_VER

class static_string
{
    const char* const p_;
    const std::size_t sz_;

public:
    typedef const char* const_iterator;

    template <std::size_t N>
    CONSTEXPR11_TN static_string(const char(&a)[N]) NOEXCEPT_TN
        : p_(a)
        , sz_(N-1)
        {}

    CONSTEXPR11_TN static_string(const char* p, std::size_t N) NOEXCEPT_TN
        : p_(p)
        , sz_(N)
        {}

    CONSTEXPR11_TN const char* data() const NOEXCEPT_TN {return p_;}
    CONSTEXPR11_TN std::size_t size() const NOEXCEPT_TN {return sz_;}

    CONSTEXPR11_TN const_iterator begin() const NOEXCEPT_TN {return p_;}
    CONSTEXPR11_TN const_iterator end()   const NOEXCEPT_TN {return p_ + sz_;}

    CONSTEXPR11_TN char operator[](std::size_t n) const
    {
        return n < sz_ ? p_[n] : throw std::out_of_range("static_string");
    }
};

inline
std::ostream&
operator<<(std::ostream& os, static_string const& s)
{
    return os.write(s.data(), s.size());
}

template <class T>
CONSTEXPR14_TN
static_string
type_name()
{
#ifdef __clang__
    static_string p = __PRETTY_FUNCTION__;
    return static_string(p.data() + 31, p.size() - 31 - 1);
#elif defined(__GNUC__)
    static_string p = __PRETTY_FUNCTION__;
#  if __cplusplus < 201402
    return static_string(p.data() + 36, p.size() - 36 - 1);
#  else
    return static_string(p.data() + 46, p.size() - 46 - 1);
#  endif
#elif defined(_MSC_VER)
    static_string p = __FUNCSIG__;
    return static_string(p.data() + 38, p.size() - 38 - 7);
#endif
}

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()
{
    using namespace 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 < 201402
    return string_view(p.data() + 36, p.size() - 36 - 1);
#  else
    return 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.

Howard Hinnant
sumber
4
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?
Lyberta
231

Mencoba:

#include <typeinfo>

// …
std::cout << typeid(a).name() << '\n';

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.

Konrad Rudolph
sumber
4
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*>'
NickV
sumber
2
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.

#include <iostream>
#include <typeinfo>

using namespace std;

int main() {
  int i;
  cout << typeid(i).name();
  return 0;
}
mdec
sumber
36

Menurut solusi Howard , jika Anda tidak menginginkan angka ajaib, saya pikir ini adalah cara yang baik untuk mewakili dan terlihat intuitif:

#include <string_view>

template <typename T>
constexpr std::string_view 
type_name()
{
    std::string_view name, prefix, suffix;
#ifdef __clang__
    name = __PRETTY_FUNCTION__;
    prefix = "std::string_view type_name() [T = ";
    suffix = "]";
#elif defined(__GNUC__)
    name = __PRETTY_FUNCTION__;
    prefix = "constexpr std::string_view type_name() [with T = ";
    suffix = "; std::string_view = std::basic_string_view<char>]";
#elif defined(_MSC_VER)
    name = __FUNCSIG__;
    prefix = "class std::basic_string_view<char,struct std::char_traits<char> > __cdecl type_name<";
    suffix = ">(void)";
#endif
    name.remove_prefix(prefix.size());
    name.remove_suffix(suffix.size());
    return name;
}
康 桓 瑋
sumber
4
Ini adalah distilasi besar upaya selama beberapa versi C ++ terakhir menjadi sesuatu yang pendek dan manis. +1.
einpoklum
1
Ini juga favorit saya!
Howard Hinnant
1
Di sini fungsi serupa yang saya gunakan, yang mendeteksi akhiran / awalan secara otomatis: stackoverflow.com/questions/1055452/…
HolyBlackCat
22

Perhatikan bahwa nama-nama yang dihasilkan oleh fitur RTTI dari C ++ tidak portabel. Misalnya kelas

MyNamespace::CMyContainer<int, test_MyNamespace::CMyObject>

akan memiliki nama-nama berikut:

// MSVC 2003:
class MyNamespace::CMyContainer[int,class test_MyNamespace::CMyObject]
// G++ 4.2:
N8MyNamespace8CMyContainerIiN13test_MyNamespace9CMyObjectEEE

Jadi, Anda tidak dapat menggunakan informasi ini untuk serialisasi. Tapi tetap saja, properti typeid (a) .name () masih bisa digunakan untuk keperluan log / debug

paercebal
sumber
19

Anda dapat menggunakan templat.

template <typename T> const char* typeof(T&) { return "unknown"; }    // default
template<> const char* typeof(int&) { return "int"; }
template<> const char* typeof(float&) { return "float"; }

Pada contoh di atas, ketika jenis tidak cocok itu akan mencetak "tidak dikenal".

Nick
sumber
3
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;

  // unmangled
  int 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);

 return 0;

}

ipapadop
sumber
10

Anda bisa menggunakan kelas ciri untuk ini. Sesuatu seperti:

#include <iostream>
using namespace std;

template <typename T> class type_name {
public:
    static const char *name;
};

#define DECLARE_TYPE_NAME(x) template<> const char *type_name<x>::name = #x;
#define GET_TYPE_NAME(x) (type_name<typeof(x)>::name)

DECLARE_TYPE_NAME(int);

int main()
{
    int a = 12;
    cout << GET_TYPE_NAME(a) << endl;
}

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

Greg Hewgill
sumber
10

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>

using namespace std;
using boost::typeindex::type_id_with_cvr;

int main() {
  int i = 0;
  const int 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';
  return 0;
}
kode tempat
sumber
1
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:

#include <iostream>
#include <typeinfo>
#include <string>

using namespace std;

int main() {
  auto x = 1;
  string my_type = typeid(x).name();
  system(("echo " + my_type + " | c++filt -t").c_str());
  return 0;
}

Diuji hanya di linux.

Alan
sumber
1
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:

#include <iostream>
#include <string_view>

using namespace std;

class probe_type;

template <typename T>
constexpr string_view type_name() {
  string_view probe_type_name("class probe_type");
  const string_view class_specifier("class");

  string_view name;
#ifdef __clang__
  name = __PRETTY_FUNCTION__;
  probe_type_name.remove_prefix(class_specifier.length());
#elif defined(__GNUC__)
  name = __PRETTY_FUNCTION__;
  probe_type_name.remove_prefix(class_specifier.length());
#elif defined(_MSC_VER)
  name = __FUNCSIG__;
#endif

  if (name.find(probe_type_name) != string_view::npos)
    return name;

  const string_view probe_type_raw_name = type_name<probe_type>();

  const size_t prefix_size = probe_type_raw_name.find(probe_type_name);

  name.remove_prefix(prefix_size);
  name.remove_suffix(probe_type_raw_name.length() - prefix_size - probe_type_name.length());

  return name;
}

class test;

int main() {
  cout << type_name<test>() << endl;

  cout << type_name<const int*&>() << endl;
  cout << type_name<unsigned int>() << endl;

  const int ic = 42;
  const int* pic = &ic;
  const int*& rpic = pic;
  cout << type_name<decltype(ic)>() << endl;
  cout << type_name<decltype(pic)>() << endl;
  cout << type_name<decltype(rpic)>() << endl;

  cout << type_name<probe_type>() << endl;
}

Keluaran

gcc 10.0.0 20190919 Wandbox:

 test
 const int *&
 unsigned int
 const int
 const int *
 const int *&
 constexpr std::string_view type_name() [with T = probe_type; std::string_view = std::basic_string_view<char>]

dentang 10.0.0 Wandbox:

 test
 const int *&
 unsigned int
 const int
 const int *
 const int *&
 std::__1::string_view type_name() [T = probe_type]

VS 2019 versi 16.3.3:

class test
const int*&
unsigned int
const int
const int*
const int*&
class std::basic_string_view<char,struct std::char_traits<char> > __cdecl type_name<class probe_type>(void)
Val
sumber
5

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>
{
    static const char* 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

template <typename T>
const char* get_type_as_string(const T&)
{
    return type_as_string<T>::value;
}
James Hopkin
sumber
1
Poin bagus tentang koma, saya tahu ada alasan mengapa makro adalah ide yang buruk tetapi tidak memikirkannya saat itu!
Greg Hewgill
2
static const char * value = "Wibble"; Anda tidak dapat melakukan pasangan itu :)
Johannes Schaub - litb
5

Solusi yang lebih umum tanpa kelebihan fungsi dari yang sebelumnya:

template<typename T>
std::string TypeOf(T){
    std::string Type="unknown";
    if(std::is_same<T,int>::value) Type="int";
    if(std::is_same<T,std::string>::value) Type="String";
    if(std::is_same<T,MyClass>::value) Type="MyClass";

    return Type;}

Di sini MyClass adalah kelas yang ditentukan pengguna. Lebih banyak kondisi dapat ditambahkan di sini juga.

Contoh:

#include <iostream>



class MyClass{};


template<typename T>
std::string TypeOf(T){
    std::string Type="unknown";
    if(std::is_same<T,int>::value) Type="int";
    if(std::is_same<T,std::string>::value) Type="String";
    if(std::is_same<T,MyClass>::value) Type="MyClass";
    return Type;}


int main(){;
    int a=0;
    std::string s="";
    MyClass my;
    std::cout<<TypeOf(a)<<std::endl;
    std::cout<<TypeOf(s)<<std::endl;
    std::cout<<TypeOf(my)<<std::endl;

    return 0;}

Keluaran:

int
String
MyClass
Jahid
sumber
5

Saya suka metode Nick, Bentuk lengkap mungkin ini (untuk semua tipe data dasar):

template <typename T> const char* typeof(T&) { return "unknown"; }    // default
template<> const char* typeof(int&) { return "int"; }
template<> const char* typeof(short&) { return "short"; }
template<> const char* typeof(long&) { return "long"; }
template<> const char* typeof(unsigned&) { return "unsigned"; }
template<> const char* typeof(unsigned short&) { return "unsigned short"; }
template<> const char* typeof(unsigned long&) { return "unsigned long"; }
template<> const char* typeof(float&) { return "float"; }
template<> const char* typeof(double&) { return "double"; }
template<> const char* typeof(long double&) { return "long double"; }
template<> const char* typeof(std::string&) { return "String"; }
template<> const char* typeof(char&) { return "char"; }
template<> const char* typeof(signed char&) { return "signed char"; }
template<> const char* typeof(unsigned char&) { return "unsigned char"; }
template<> const char* typeof(char*&) { return "char*"; }
template<> const char* typeof(signed char*&) { return "signed char*"; }
template<> const char* typeof(unsigned char*&) { return "unsigned char*"; }
Jahid
sumber
2
(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],const int, void (*)(std::string)))(volatile int*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>

static constexpr std::size_t max_str_lit_len = 256;

template <std::size_t I, std::size_t N> constexpr char sl_at(const char (&str)[N])
{
    if constexpr(I < N)
        return str[I];
    else
        return '\0';
}

constexpr std::size_t sl_len(const char *str)
{
    for (std::size_t i = 0; i < max_str_lit_len; i++)
        if (str[i] == '\0')
            return i;
    return 0;
}

template <char ...C> struct str_lit
{
    static constexpr char value[] {C..., '\0'};
    static constexpr int 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, const char *> struct trim_str_lit_impl;
template <std::size_t ...I, const char *S> struct trim_str_lit_impl<std::index_sequence<I...>, S>
{
    using type = str_lit<S[I]...>;
};
template <std::size_t N, const char *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> constexpr auto make_str_lit(const char (&str)[N])
{
    return trim_str_lit<sl_len((const char (&)[N])str), str>{};
}

template <std::size_t A, std::size_t B> struct cexpr_pow {static constexpr std::size_t value = A * cexpr_pow<A,B-1>::value;};
template <std::size_t A> struct cexpr_pow<A,0> {static constexpr 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...>>
{
    static constexpr auto func()
    {
        if constexpr (N >= cexpr_pow<10,X>::value)
            return num_to_str_lit_impl<N, X+1>::func();
        else
            return 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> inline constexpr bool ptr_or_ref = std::is_pointer_v<T> || std::is_reference_v<T> || std::is_member_pointer_v<T>;
template <typename T> inline constexpr bool 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> inline constexpr const char *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<const 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<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);};
HolyBlackCat
sumber
2
#include <iostream>
#include <typeinfo>
using namespace 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);
    return 0;
}

Keluaran:

Type of a: St16initializer_listIPKcE
Real type of a:
std::initializer_list<char const*>
Type of s: PKc
Real type of s:
char const*
one
two
three
Type of i: i
Real type of i:
int
Serigala abu-abu
sumber
2

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(){
    const int theAnswer = 32;
    auto x = theAnswer;
    auto y = &theAnswer;
    TD<decltype(x)> xType;
    TD<decltype(y)> yType;
    return 0;
}

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<const int *> yType has incomplete type and cannot be defined TD<decltype(y)> yType;

Karenanya, kita mengetahui bahwa xtipe itu adalah int, ytipe ituconst int*

Milo Lu
sumber
0

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

Lars Melchior
sumber