Mengurai hasil std :: type_info :: name

94

Saat ini saya sedang mengerjakan beberapa kode logging yang seharusnya - antara lain - mencetak informasi tentang fungsi panggilan. Ini seharusnya relatif mudah, C ++ standar memiliki type_infokelas. Ini berisi nama kelas / fungsi / etc yang diketik. tapi itu hancur. Itu tidak terlalu berguna. Ie typeid(std::vector<int>).name()kembali St6vectorIiSaIiEE.

Adakah cara untuk menghasilkan sesuatu yang bermanfaat dari ini? Seperti std::vector<int>contoh di atas. Jika hanya berfungsi untuk kelas non-template, itu juga bagus.

Solusinya seharusnya berfungsi untuk gcc, tetapi akan lebih baik jika saya bisa mem-port-nya. Ini untuk pencatatan jadi tidak terlalu penting sehingga tidak bisa dimatikan, tapi harus membantu untuk debugging.

ujung
sumber

Jawaban:

118

Mengingat perhatian yang diterima pertanyaan / jawaban ini, dan umpan balik yang berharga dari GManNickG , saya telah sedikit membersihkan kode. Dua versi diberikan: satu dengan fitur C ++ 11 dan satu lagi dengan hanya fitur C ++ 98.

Dalam tipe file.hpp

#ifndef TYPE_HPP
#define TYPE_HPP

#include <string>
#include <typeinfo>

std::string demangle(const char* name);

template <class T>
std::string type(const T& t) {

    return demangle(typeid(t).name());
}

#endif

Dalam file type.cpp (membutuhkan C ++ 11)

#include "type.hpp"
#ifdef __GNUG__
#include <cstdlib>
#include <memory>
#include <cxxabi.h>

std::string demangle(const char* name) {

    int status = -4; // some arbitrary value to eliminate the compiler warning

    // enable c++11 by passing the flag -std=c++11 to g++
    std::unique_ptr<char, void(*)(void*)> res {
        abi::__cxa_demangle(name, NULL, NULL, &status),
        std::free
    };

    return (status==0) ? res.get() : name ;
}

#else

// does nothing if not g++
std::string demangle(const char* name) {
    return name;
}

#endif

Pemakaian:

#include <iostream>
#include "type.hpp"

struct Base { virtual ~Base() {} };

struct Derived : public Base { };

int main() {

    Base* ptr_base = new Derived(); // Please use smart pointers in YOUR code!

    std::cout << "Type of ptr_base: " << type(ptr_base) << std::endl;

    std::cout << "Type of pointee: " << type(*ptr_base) << std::endl;

    delete ptr_base;
}

Ini mencetak:

Type of ptr_base: Base*
Jenis pointee:Derived

Diuji dengan g ++ 4.7.2, g ++ 4.9.0 20140302 (eksperimental), clang ++ 3.4 (trunk 184647), clang 3.5 (trunk 202594) pada Linux 64 bit dan g ++ 4.7.2 (Mingw32, Win32 XP SP2).

Jika Anda tidak dapat menggunakan fitur C ++ 11, berikut adalah cara melakukannya di C ++ 98, file type.cpp sekarang:

#include "type.hpp"
#ifdef __GNUG__
#include <cstdlib>
#include <memory>
#include <cxxabi.h>

struct handle {
    char* p;
    handle(char* ptr) : p(ptr) { }
    ~handle() { std::free(p); }
};

std::string demangle(const char* name) {

    int status = -4; // some arbitrary value to eliminate the compiler warning

    handle result( abi::__cxa_demangle(name, NULL, NULL, &status) );

    return (status==0) ? result.p : name ;
}

#else

// does nothing if not g++
std::string demangle(const char* name) {
    return name;
}

#endif


(Pembaruan dari 8 Sep 2013)

Jawaban yang diterima (per 7 Sep 2013) , ketika panggilan ke abi::__cxa_demangle()berhasil, mengembalikan pointer ke array yang dialokasikan tumpukan lokal ... aduh!
Perhatikan juga bahwa jika Anda menyediakan buffer, abi::__cxa_demangle()asumsikan buffer tersebut dialokasikan di heap. Mengalokasikan buffer pada tumpukan adalah bug (dari gnu doc): "Jika output_buffertidak cukup lama, ini akan diperluas menggunakan realloc." Memanggil realloc()penunjuk ke tumpukan ... aduh! (Lihat juga komentar baik Igor Skochinsky .)

Anda dapat dengan mudah memverifikasi kedua bug ini: cukup kurangi ukuran buffer dalam jawaban yang diterima (mulai 7 Sep 2013) dari 1024 menjadi sesuatu yang lebih kecil, misalnya 16, dan berikan sesuatu dengan nama tidak lebih dari 15 (begitu realloc()juga tidak dipanggil). Namun, tergantung pada sistem Anda dan pengoptimalan compiler, outputnya adalah: sampah / nothing / program crash.
Untuk memverifikasi bug kedua: setel ukuran buffer ke 1 dan panggil dengan sesuatu yang namanya lebih dari 1 karakter. Ketika Anda menjalankannya, program hampir pasti macet saat mencoba memanggil realloc()dengan pointer ke stack.


(Jawaban lama dari 27 Desember 2010)

Perubahan penting yang dibuat pada kode KeithB : buffer harus dialokasikan oleh malloc atau ditentukan sebagai NULL. JANGAN mengalokasikannya di tumpukan.

Sebaiknya periksa juga status itu.

Saya gagal menemukan HAVE_CXA_DEMANGLE. Saya memeriksa __GNUG__meskipun itu tidak menjamin bahwa kode bahkan akan dikompilasi. Ada yang punya ide yang lebih baik?

#include <cxxabi.h>

const string demangle(const char* name) {

    int status = -4;

    char* res = abi::__cxa_demangle(name, NULL, NULL, &status);

    const char* const demangled_name = (status==0)?res:name;

    string ret_val(demangled_name);

    free(res);

    return ret_val;
}
Ali
sumber
Perhatikan bahwa ini perlu #include <cxxabi.h>. Jika tidak bekerja dengan baik, terima kasih.
jterrace
2
Dari dokumen : output_bufferWilayah memori, dialokasikan dengan malloc, dengan panjang * byte, tempat penyimpanan nama yang telah dipisahkan. Jika output_buffer tidak cukup panjang, maka akan diperluas menggunakan realloc. output_buffer bisa jadi NULL; dalam hal ini, nama yang telah didemangifikasi ditempatkan di wilayah memori yang dialokasikan dengan malloc.
Igor Skochinsky
2
@IgorSkochinsky Ya, ada kesalahan ketik pada komentar saya sebelumnya, tetapi saya tidak dapat mengeditnya. Apa yang ingin saya tulis: "Terakhir kali saya memeriksa abi::__cxa_demanglemengharapkannya untuk dialokasikan di heap. " Terima kasih banyak telah mencari dok!
Ali
1
Perhatikan bahwa secara teknis ini bisa bocor jika ret_valterlempar selama konstruksi. Anda dapat menggunakan pelindung teropong untuk menjaganya.
GManNickG
3
Jika mungkin akan lebih jelas untuk digunakan std::unique_ptr<char, decltype(&std::free)>sebagai tanda tangan untuk pointer Anda.
mindvirus
28

Inti pendorong berisi demangler. Checkout core / demangle.hpp :

#include <boost/core/demangle.hpp>
#include <typeinfo>
#include <iostream>

template<class T> struct X
{
};

int main()
{
    char const * name = typeid( X<int> ).name();

    std::cout << name << std::endl; // prints 1XIiE
    std::cout << boost::core::demangle( name ) << std::endl; // prints X<int>
}

Ini pada dasarnya hanya pembungkus untuk abi::__cxa_demangle, seperti yang telah disarankan sebelumnya.

moof2k
sumber
1
Jika dorongan adalah pilihan, ini adalah cara terbaik untuk pergi!
hbobenicio
14

Inilah yang kami gunakan. HAVE_CXA_DEMANGLE hanya disetel jika tersedia (hanya GCC versi terbaru).

#ifdef HAVE_CXA_DEMANGLE
const char* demangle(const char* name)
{
   char buf[1024];
    unsigned int size=1024;
    int status;
    char* res = abi::__cxa_demangle (name,
                                 buf,
                                 &size,
                                 &status);
    return res;
  }
#else
const char* demangle(const char* name)
{
  return name;
}
#endif  
KeithB
sumber
6
Anda perlu memasukkan #include <cxxabi.h>.
fuenfundachtzig
Menarik. Saya memiliki __cxa_demangle tanpa HAVE_CXA_DEMANGLE ditentukan
mkb
@Matt Yang ingin saya katakan adalah bahwa sistem build kami, berdasarkan autoconf, hanya menyetel HAVE_CXA_DEMANGLE jika tersedia.
KeithB
19
PERINGATAN! Kode di atas kemungkinan besar akan membuat program crash. Buffer harus dialokasikan oleh malloc atau ditentukan sebagai NULL. JANGAN mengalokasikannya di tumpukan. Lihat kode saya di bawah ini.
Ali
hati-hati, res bisa mengembalikan NULL :)
Zibri
9

Di sini, lihat type_strings.hpp yang berisi fungsi yang melakukan apa yang Anda inginkan.

Jika Anda hanya mencari alat demangling, yang misalnya dapat Anda gunakan untuk mengacaukan hal-hal yang ditampilkan dalam file log, lihatlah c++filt, yang disertakan dengan binutils. Itu dapat mendemangle C ++ dan nama simbol Java.

Johannes Schaub - litb
sumber
Sebagai catatan, cxa_demange () (yang ditautkan oleh kode) dan file cx ++ adalah khusus untuk gcc. Tidak ada cara portabel untuk melakukan ini.
KeithB
c ++ filt tidak memotongnya, saya membutuhkan hal ini (atau sebagian besar) pada waktu kompilasi, sebagian besar dilakukan dengan makro.
berakhir
4
Tautan ke type_strings.cpp tampaknya rusak.
StackedCrooked
1
Hai @GregoryPakosz Tautan github di komentar Anda di atas sepertinya juga rusak :( Cheers
olibre
Hanya FYI penting: abi::__cxa_demangle()dan sejenisnya dari <cxxabi.h> tidak spesifik GCC - mereka mungkin hanya GCC di masa lalu, tetapi pada saat posting ini ditulis, <cxxabi.h>adalah standar ad-hoc yang mengakar. Jadi, meskipun tautan kode jawabannya adalah DOI, saya dapat menjamin bahwa Clang memberikan dukungan kelas satu dalam kasus ini… qv, dari sumber Clang libcxxabi: masing-masing menyatakan, impl, tes besar: git.io/vRTBo , git.io/vRTBh , git.io/vRTRf - komentar kode pengujian mencatat bahwa implementasi Clang mampu melakukan lebih banyak demangling, dibandingkan dengan GCC.
fish2000
4

Implementasinya telah ditentukan, jadi ini bukan sesuatu yang portabel. Di MSVC ++, name () adalah nama tanpa dekorasi, dan Anda harus melihat raw_name () untuk mendapatkan nama yang didekorasi.
Hanya sedikit dalam gelap di sini, tetapi di bawah gcc, Anda mungkin ingin melihat demangle.h

Gerhana
sumber
3

Bukan solusi lengkap, tetapi Anda mungkin ingin melihat apa yang ditentukan oleh beberapa makro standar (atau yang didukung secara luas). Ini umum dalam kode logging untuk melihat penggunaan makro:

__FUNCTION__
__FILE__
__LINE__

e.g.:

log(__FILE__, __LINE__, __FUNCTION__, mymessage);
luke
sumber
4
Belum lagi PRETTY_FUNCTION .
CesarB
1
Ini akan memberi Anda informasi tentang posisi Anda dalam kode. Apa yang ditanyakan pertanyaan itu adalah nama tipe yang cantik, seperti std :: vector.
KeithB
Dia menyebutkan itu untuk debugging, dan saya menyatakan itu bukan solusi lengkap. Makro lain seperti FUNCDNAME akan mengembalikan nama dekorasi.
Lukas
Sebenarnya, membaca ulang pertanyaan, itu adalah "Saya sedang mengerjakan beberapa kode logging yang seharusnya - antara lain - mencetak informasi tentang fungsi panggilan." Ini bekerja.
Max Lybbert
Itu belum lengkap, karena saya tidak tahu namespace-nya. Ini semua sudah ada di kode saya. Tapi Terimakasih.
akhir
3

Saya juga menemukan makro yang disebut __PRETTY_FUNCTION__, yang melakukan triknya. Ini memberi nama fungsi yang cantik (gambar :)). Inilah yang saya butuhkan.

Yaitu memberi saya yang berikut:

virtual bool mutex::do_unlock()

Tapi menurut saya ini tidak berfungsi pada kompiler lain.

ujung
sumber
Ya, PRETTY_FUNCTION khusus untuk gcc.
Greg Rogers
2

Sedikit variasi pada solusi Ali. Jika Anda ingin kodenya masih sangat mirip

typeid(bla).name(),

menulis ini sebagai gantinya

Typeid(bla).name() (hanya berbeda dalam huruf kapital pertama)

maka Anda mungkin tertarik dengan ini:

Dalam tipe file.hpp

#ifndef TYPE_HPP
#define TYPE_HPP

#include <string>
#include <typeinfo>

std::string demangle(const char* name);

/*
template <class T>
std::string type(const T& t) {

  return demangle(typeid(t).name());
}
*/

class Typeid {
 public:

  template <class T>
    Typeid(const T& t) : typ(typeid(t)) {}

  std::string name() { return demangle(typ.name()); }

 private:
  const std::type_info& typ;
};


#endif

type.cpp tetap sama seperti di solusi Ali

matzzz.dll
sumber
1

Lihatlah __cxa_demangleyang dapat Anda temukan di cxxabi.h.

CesarB
sumber
Saya ambil, sudah usang, sesuai dengan pesan yang saya dapatkan.
berakhir
Di mana Anda menemukan pesan itu? Saya baru saja mencarinya di Google dan tampaknya didukung, tidak ada bukti yang tidak digunakan lagi.
Ali
Mungkin tidak digunakan lagi di :: namespace. Gunakan abi :: __ cxa_demangle dan Anda tidak akan mendapatkan peringatan. Gcc apa yang Anda gunakan?
Onitake
1
// KeithB's solution is good, but has one serious flaw in that unless buf is static
// it'll get trashed from the stack before it is returned in res - and will point who-knows-where
// Here's that problem fixed, but the code is still non-re-entrant and not thread-safe.
// Anyone care to improve it?

#include <cxxabi.h>

// todo: javadoc this properly
const char* demangle(const char* name)
{
    static char buf[1024];
    size_t size = sizeof(buf);
    int status;
    // todo:
    char* res = abi::__cxa_demangle (name,
                                 buf,
                                 &size,
                                 &status);
    buf[sizeof(buf) - 1] = 0; // I'd hope __cxa_demangle does this when the name is huge, but just in case.
    return res;
  }
Dan Dare
sumber
11
PERINGATAN! Buffer harus dialokasikan oleh malloc atau ditentukan sebagai NULL. JANGAN mengalokasikannya di tumpukan. Lihat kode saya di bawah ini.
Ali
1

The solusi yang diterima [1] kebanyakan bekerja dengan baik. Saya menemukan setidaknya satu kasus (dan saya tidak akan menyebutnya kasus sudut) yang tidak melaporkan apa yang saya harapkan ... dengan referensi.

Untuk kasus tersebut, saya menemukan solusi lain, diposting di bagian bawah.

Kasus bermasalah (menggunakan typeseperti yang didefinisikan dalam [1]):

int i = 1;
cout << "Type of " << "i" << " is " << type(i) << endl;
int & ri = i;
cout << "Type of " << "ri" << " is " << type(ri) << endl;

menghasilkan

Type of i is int
Type of ri is int

Solusi (menggunakan type_name<decltype(obj)>(), lihat kode di bawah):

cout << "Type of " << "i" << " is " << type_name<decltype(i)>() << endl;
cout << "Type of " << "ri" << " is " << type_name<decltype(ri)>() << endl;

menghasilkan

Type of i is int
Type of ri is int&

seperti yang diinginkan (setidaknya oleh saya)

Kode . Itu harus dalam tajuk yang disertakan, bukan dalam sumber yang dikompilasi secara terpisah, karena masalah spesialisasi. Lihat referensi yang tidak ditentukan untuk fungsi template misalnya.

#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;
}
sancho.s ReinstateMonicaCellio
sumber
0

Saya selalu ingin menggunakan type_info, tetapi saya yakin bahwa hasil dari fungsi anggota name () tidak standar dan tidak akan selalu mengembalikan apa pun yang dapat diubah menjadi hasil yang bermakna.
Jika Anda menggunakan satu kompilator, mungkin ada fungsi khusus kompilator yang akan melakukan apa yang Anda inginkan. Periksa dokumentasinya.

quamrana
sumber
0

Mengikuti solusi Ali, berikut adalah alternatif template C ++ 11 yang bekerja paling baik untuk penggunaan saya.

// type.h
#include <cstdlib>
#include <memory>
#include <cxxabi.h>

template <typename T>
std::string demangle() {
  int status = -4;

  std::unique_ptr<char, void (*)(void*)> res{
      abi::__cxa_demangle(typeid(T).name(), NULL, NULL, &status), std::free};
  return (status == 0) ? res.get() : typeid(T).name();
}

Pemakaian:

// main.cpp
#include <iostream>

namespace test {
    struct SomeStruct {};
}

int main()
{
    std::cout << demangle<double>() << std::endl;
    std::cout << demangle<const int&>() << std::endl;
    std::cout << demangle<test::SomeStruct>() << std::endl;

    return 0;
}

Akan mencetak:

double                                                                        
int                                                                           
test::SomeStruct
Alexis Paques
sumber