Std :: type_info :: name sonucunun çözülmesi


97

Şu anda - diğer şeylerin yanı sıra - çağıran işlev hakkında bilgi yazdırması gereken bazı günlük kaydı kodu üzerinde çalışıyorum. Bu nispeten kolay olmalı, standart C ++ 'nın bir type_infosınıfı vardır. Bu, typeid'li sınıf / işlev / vb.'nin adını içerir. ama ezilmiş. Pek kullanışlı değil. Yani typeid(std::vector<int>).name()dönüyor St6vectorIiSaIiEE.

Bundan faydalı bir şey üretmenin bir yolu var mı? Gibi std::vector<int>yukarıdaki örneğin. Yalnızca şablon olmayan sınıflar için çalışıyorsa, bu da sorun değil.

Çözüm gcc için çalışmalıdır, ancak onu taşıyabilirsem daha iyi olur. Günlüğe kaydetmek içindir, bu nedenle kapatılamayacak kadar önemli değildir, ancak hata ayıklama için yardımcı olmalıdır.

Yanıtlar:


119

Bu sorunun / cevabın gördüğü ilgi ve GManNickG'den gelen değerli geri bildirimler göz önüne alındığında, kodu biraz temizledim. İki sürüm verilmiştir: biri C ++ 11 özellikli ve diğeri yalnızca C ++ 98 özellikli.

Dosya type.hpp içinde

#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

Dosya type.cpp'de (C ++ 11 gerektirir)

#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

Kullanım:

#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;
}

Aşağıdakileri yazdırır:

Ptr_base türü: Pointe Base*
türü:Derived

Linux 64 bit üzerinde g ++ 4.7.2, g ++ 4.9.0 20140302 (deneysel), clang ++ 3.4 (trunk 184647), clang 3.5 (trunk 202594) ve g ++ 4.7.2 (Mingw32, Win32 XP SP2) ile test edilmiştir.

C ++ 11 özelliklerini kullanamıyorsanız, C ++ 98'de şu şekilde yapılabilir, dosya type.cpp şu anda:

#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


(8 Eylül 2013'ten itibaren güncelleme)

Kabul edilen yanıt (7 Eylül 2013 itibariyle) , çağrı abi::__cxa_demangle()başarılı olduğunda, yerel, yığın ayrılmış diziye bir işaretçi döndürür ... ouch!
Ayrıca bir arabellek sağlarsanız abi::__cxa_demangle(), yığın üzerinde tahsis edileceğini varsaydığını unutmayın . Tamponun yığın üzerine tahsis edilmesi bir hatadır (gnu belgesinden): " output_bufferYeterince uzun değilse, kullanılarak genişletilir realloc." Yığına realloc()bir işaretçi çağırmak ... ah! (Ayrıca Igor Skochinsky'nin nazik yorumuna bakın.)

Sadece örnek 16 için, daha küçük bir şeye 1024 den (7 Eyl 2013 itibariyle) kabul cevap tampon boyutunu küçültmek ve bir adla o şey vermek: Kolayca bu hataların ikisi doğrulamak değil bu yüzden (uzun 15'ten realloc()olduğunu değil ) olarak adlandırılan. Yine de, sisteminize ve derleyici optimizasyonlarına bağlı olarak çıktı şu olacaktır: çöp / hiçbir şey / program çökmesi.
İkinci hatayı doğrulamak için: tampon boyutunu 1 olarak ayarlayın ve adı 1 karakterden uzun olan bir şeyle çağırın. Çalıştırdığınızda, program realloc()yığına bir işaretçi ile çağrı yapmaya çalışırken neredeyse kesinlikle çöküyor .


(27 Aralık 2010 tarihli eski cevap)

KeithB kodunda yapılan önemli değişiklikler : arabellek ya malloc tarafından tahsis edilmeli ya da NULL olarak belirtilmelidir. Onu yığına tahsis ETMEYİN.

Bu durumu da kontrol etmek akıllıca olacaktır.

Ben bulamadılar HAVE_CXA_DEMANGLE. Kontrol ettim, __GNUG__ancak bu kodun derleneceğini garanti etmiyor. Daha iyi bir fikri olan var mı?

#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;
}

Bunun gerekli olduğuna dikkat edin #include <cxxabi.h>. Aksi takdirde harika çalıştı, teşekkürler.
jterrace

2
Gönderen docs : output_bufferBelleğin bir bölge, demangled adı kaydedileceği * uzunluk byte, malloc ile tahsis etti. Output_buffer yeterince uzun değilse, realloc kullanılarak genişletilir. output_buffer bunun yerine NULL olabilir; bu durumda, demangled adı malloc ile ayrılmış bir bellek bölgesine yerleştirilir.
Igor Skochinsky

2
@IgorSkochinsky Evet, önceki yorumumda bir yazım hatası var ama bunu düzenleyemiyorum. Yazmak istediğim şey: "En son kontrol ettiğimde yığınaabi::__cxa_demangle tahsis edilmesini bekledim . " Dokümana baktığınız için çok teşekkür ederim!
Ali

1
ret_valİnşaat sırasında fırlatılırsa teknik olarak bunun sızabileceğini unutmayın . Buna karşı korunmak için bir dürbün koruması kullanabilirsiniz.
GManNickG

3
std::unique_ptr<char, decltype(&std::free)>İşaretçinizin imzası olarak kullanmak muhtemelen daha net olacaktır .
mindvirus

29

Boost çekirdeği bir demangler içerir. 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>
}

Daha abi::__cxa_demangleönce önerildiği gibi, temelde sadece bir paketleyicidir .


1
Güçlendirme bir seçenekse, gitmenin en iyi yolu budur!
hbobenicio

13

Kullandığımız şey bu. HAVE_CXA_DEMANGLE yalnızca varsa ayarlanır (yalnızca GCC'nin son sürümleri).

#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  

6
Dahil etmelisiniz #include <cxxabi.h>.
fuenfundachtzig

İlginç. HAVE_CXA_DEMANGLE tanımlanmamış __cxa_demangle var
mkb

@Matt Söylemek istediğim, autoconf'a dayalı derleme sistemimiz, eğer mevcutsa, sadece HAVE_CXA_DEMANGLE ayarlıyor.
KeithB

19
UYARI! Yukarıdaki kodun programın çökmesine neden olması muhtemeldir. Tamponun malloc tarafından tahsis edilmesi veya NULL olarak belirtilmesi gerekir. Onu yığına tahsis ETMEYİN. Aşağıdaki koduma bakın.
Ali

dikkat, res NULL döndürebilir :)
Zibri

9

Burada type_strings.hpp'ye bir göz atın, istediğinizi yapan bir işlev içerir.

Örneğin, bir günlük dosyasında gösterilen şeyleri çözmek için kullanabileceğiniz bir demangling aracı arıyorsanız, c++filtbinutils ile birlikte gelen bir göz atın . C ++ ve Java sembol adlarını ayrıştırabilir.


Not etmek gerekirse, hem cxa_demange () (bağlantılı kodun kullandığı) hem de cx ++ filt gcc'ye özgüdür. Bunu yapmanın taşınabilir bir yolu yok.
KeithB

c ++ filt onu kesmiyor, bu şeylere (veya çoğuna) derleme zamanında ihtiyacım var, çoğunlukla makrolarla yapılır.
terminus

4
Type_strings.cpp bağlantısı bozuk görünüyor.
StackedCrooked

1
Merhaba @GregoryPakosz Yukarıdaki yorumunuzdaki github bağlantısı da bozuk görünüyor :( Şerefe
oHo

Sadece önemli bir bilgi notu: abi::__cxa_demangle()ve kaynağı <cxxabi.h> GCC'ye özgü değil - yalnızca uzak geçmişte GCC olmuş olabilirler, ancak bu yazının yazıldığı zamana göre, <cxxabi.h>yerleşik bir şekilde geçici bir standarttı. Bu yüzden, yanıtlayanın kod bağlantısı DOI iken , Clang'ın bu durumda birinci sınıf destek sağladığına kefil olabilirim… qv, Clang'ınlibcxxabi kaynağından: ilgili decl, impl , büyük test: git.io/vRTBo , git.io/vRTBh , git.io/vRTRf - test kodunun yorumları, Clang uygulamasının GCC'ye kıyasla bir şekilde daha fazla ayrıştırma yeteneğine sahip olduğunu belirtir.
fish2000

5

Uygulama tanımlı, bu yüzden taşınabilir olacak bir şey değil. MSVC ++ 'da, isim () süslenmemiş isimdir ve dekore edilmiş olanı elde etmek için raw_name ()' e bakmanız gerekir.
Burada karanlıkta bir bıçak var, ancak gcc altında, demangle.h'ye bakmak isteyebilirsiniz.


3

Ayrıca __PRETTY_FUNCTION__hile yapan bir makro da buldum . Güzel bir fonksiyon adı verir (rakamlar :)). İhtiyacım olan şey bu.

Yani bana şunu veriyor:

virtual bool mutex::do_unlock()

Ancak diğer derleyicilerde çalıştığını sanmıyorum.


Evet, PRETTY_FUNCTION gcc'ye özeldir.
Greg Rogers

2

Tam bir çözüm değil, ancak bazı standart (veya yaygın olarak desteklenen) makronun tanımladığına bakmak isteyebilirsiniz. Makroların kullanımını görmek günlük kodunda yaygındır:

__FUNCTION__
__FILE__
__LINE__

e.g.:

log(__FILE__, __LINE__, __FUNCTION__, mymessage);

4
PRETTY_FUNCTION bahsetmiyorum bile .
CesarB

1
Bu size kodun neresinde olduğunuz hakkında bilgi verecektir. Sorunun sorduğu şey, std :: vector gibi güzel bir tür adıydı.
KeithB

Hata ayıklama için olduğundan bahsetti ve tam bir çözüm olmadığını belirttim. FUNCDNAME gibi diğer makrolar süslenmiş adı döndürecektir.
luke

Aslında soruyu yeniden okurken, "Şu anda - diğer şeylerin yanı sıra - çağıran işlevle ilgili bilgileri yazdırması gereken bir günlük kodu üzerinde çalışıyorum." Bu çalışıyor.
Max Lybbert

İsim alanını bilmediğim için tamamlanmadı. Bu benim kodumda zaten var. Yinede teşekkürler.
terminali

2

Ali'nin çözümünde küçük bir değişiklik. Kodun hala çok benzer olmasını istiyorsanız

typeid(bla).name(),

bunun yerine bunu yazmak

Typeid(bla).name() (sadece büyük ilk harf farklıdır)

o zaman bununla ilgilenebilirsiniz:

Dosya type.hpp içinde

#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 , Ali'nin çözümünde olduğu gibi kalır


1

Bulabileceğiniz bir göz __cxa_demangleatın cxxabi.h.


Aldım, aldığım mesaja göre kullanımdan kaldırıldı.
terminali

Bu mesajı nereden buldunuz? Google'da araştırdım ve destekleniyor gibi görünüyor, kullanımdan kaldırıldığına dair bir kanıt yok.
Ali

Belki :: ad alanında kullanımdan kaldırılmıştır. Abi :: __ cxa_demangle kullanın ve bir uyarı almayacaksınız. Hangi gcc kullanıyorsunuz?
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;
  }

11
UYARI! Tampon, malloc tarafından tahsis edilmeli veya NULL olarak belirtilmelidir. Onu yığına tahsis ETMEYİN. Aşağıdaki koduma bakın.
Ali

1

Kabul edilen çözüm [1] çoğunlukla işe yarar. En az bir vaka buldum (ve buna köşe vaka demezdim) beklediğim şeyi referanslarla ... rapor etmedi.

Bu durumlar için altta yayınlanan başka bir çözüm buldum.

Sorunlu durum ( type[1] 'de tanımlandığı gibi kullanarak ):

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

üretir

Type of i is int
Type of ri is int

Çözüm (kullanarak type_name<decltype(obj)>(), aşağıdaki koda bakın):

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

üretir

Type of i is int
Type of ri is int&

istendiği gibi (en azından benim tarafımdan)

Kod . Uzmanlık sorunları nedeniyle ayrı olarak derlenmiş bir kaynakta değil, eklenmiş bir başlıkta olması gerekir. Örneğin şablon işlevine tanımsız referansa bakın .

#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;
}

0

Her zaman type_info kullanmak istemişimdir, ancak eminim ki name () üye işlevinin sonucu standart değildir ve anlamlı bir sonuca dönüştürülebilecek hiçbir şey döndürmeyecektir.
Bir derleyiciye bağlı kalıyorsanız, istediğinizi yapacak derleyiciye özel bir işlev olabilir. Belgeleri kontrol edin.


0

Ali'nin çözümünün ardından, kullanımım için en iyi şekilde çalışan C ++ 11 şablon alternatifi burada.

// 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();
}

Kullanım:

// 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;
}

Yazdırılacak:

double                                                                        
int                                                                           
test::SomeStruct
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.