Sbrogliare il risultato di std :: type_info :: name


93

Attualmente sto lavorando su un codice di registrazione che dovrebbe, tra le altre cose, stampare informazioni sulla funzione chiamante. Questo dovrebbe essere relativamente facile, il C ++ standard ha una type_infoclasse. Contiene il nome della classe / funzione / ecc. ma è lacerato. Non è molto utile. Cioè typeid(std::vector<int>).name()ritorna St6vectorIiSaIiEE.

C'è un modo per produrre qualcosa di utile da questo? Come std::vector<int>per l'esempio sopra. Se funziona solo per classi non modello, va bene anche questo.

La soluzione dovrebbe funzionare per gcc, ma sarebbe meglio se potessi portarlo. È per la registrazione, quindi non è così importante che non possa essere disattivato, ma dovrebbe essere utile per il debug.

Risposte:


117

Data l'attenzione che riceve questa domanda / risposta e il prezioso feedback di GManNickG , ho ripulito un po 'il codice. Vengono fornite due versioni: una con funzionalità C ++ 11 e un'altra con solo funzionalità C ++ 98.

Nel file type.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

Nel file type.cpp (richiede 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

Utilizzo:

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

Stampa:

Tipo di ptr_base: Base*
Tipo di punta:Derived

Testato con g ++ 4.7.2, g ++ 4.9.0 20140302 (sperimentale), clang ++ 3.4 (trunk 184647), clang 3.5 (trunk 202594) su Linux 64 bit e g ++ 4.7.2 (Mingw32, Win32 XP SP2).

Se non puoi utilizzare le funzionalità di C ++ 11, ecco come farlo in C ++ 98, il file type.cpp è ora:

#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


(Aggiornamento dall'8 settembre 2013)

La risposta accettata (a partire dal 7 settembre 2013) , quando la chiamata a ha abi::__cxa_demangle()esito positivo, restituisce un puntatore a un array locale allocato nello stack ... ahi!
Notare inoltre che se si fornisce un buffer, si abi::__cxa_demangle()presume che sia allocato nell'heap. Allocare il buffer sullo stack è un bug (dal documento gnu): "Se output_buffernon è abbastanza lungo, viene espanso usando realloc." Chiamare realloc()un puntatore allo stack ... ahi! (Vedi anche il gentile commento di Igor Skochinsky .)

Puoi facilmente verificare entrambi questi bug: riduci la dimensione del buffer nella risposta accettata (a partire dal 7 settembre 2013) da 1024 a qualcosa di più piccolo, ad esempio 16, e dagli un nome con un nome non più lungo di 15 (così realloc()è non chiamato). Tuttavia, a seconda del sistema e delle ottimizzazioni del compilatore, l'output sarà: spazzatura / niente / crash del programma.
Per verificare il secondo bug: imposta la dimensione del buffer su 1 e chiamalo con qualcosa il cui nome è più lungo di 1 carattere. Quando lo esegui, il programma quasi sicuramente va in crash mentre tenta di chiamare realloc()con un puntatore allo stack.


(La vecchia risposta del 27 dicembre 2010)

Importanti modifiche apportate al codice di KeithB : il buffer deve essere allocato da malloc o specificato come NULL. NON allocarlo in pila.

È consigliabile controllare anche quello stato.

Non sono riuscito a trovare HAVE_CXA_DEMANGLE. Controllo __GNUG__anche se ciò non garantisce nemmeno che il codice venga compilato. Qualcuno ha un'idea migliore?

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

Nota che questo ha bisogno di #include <cxxabi.h>. Altrimenti ha funzionato alla grande, grazie.
jterrace

2
Dalla documentazione : output_bufferuna regione di memoria, allocata con malloc, di * length byte, in cui è memorizzato il nome distrutto. Se output_buffer non è abbastanza lungo, viene espanso utilizzando realloc. output_buffer può invece essere NULL; in tal caso, il nome demangled viene posto in una regione di memoria allocata con malloc.
Igor Skochinsky

2
@IgorSkochinsky Sì, c'è un errore di battitura nel mio commento precedente ma non posso modificarlo. Quello che volevo scrivere: "L'ultima volta che ho controllato mi abi::__cxa_demangleaspettavo che fosse allocato sul mucchio. " Grazie mille per aver cercato il documento!
Ali

1
Si noti che tecnicamente questo può perdere se viene ret_vallanciato durante la costruzione. Puoi usare una protezione per mirino per proteggerti.
GManNickG

3
Se sarebbe probabilmente più chiaro da usare std::unique_ptr<char, decltype(&std::free)>come firma per il tuo puntatore.
mindvirus

27

Il nucleo del potenziamento contiene un 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>
}

È fondamentalmente solo un wrapper per abi::__cxa_demangle, come è stato suggerito in precedenza.


1
Se il boost è un'opzione, questo è il modo migliore per farlo!
hbobenicio

13

Questo è ciò che usiamo. HAVE_CXA_DEMANGLE è impostato solo se disponibile (solo versioni recenti di GCC).

#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
Devi includere #include <cxxabi.h>.
fuenfundachtzig

Interessante. Ho definito __cxa_demangle senza HAVE_CXA_DEMANGLE
mkb

@Matt Quello che volevo dire è che il nostro sistema di compilazione, basato su autoconf, imposta HAVE_CXA_DEMANGLE solo se disponibile.
KeithB

19
AVVERTIMENTO! È probabile che il codice precedente causi il crash del programma. Il buffer deve essere allocato da malloc o specificato come NULL. NON allocarlo in pila. Vedi il mio codice qui sotto.
Ali

attenzione, res potrebbe restituire NULL :)
Zibri

8

Qui, dai un'occhiata a type_strings.hpp che contiene una funzione che fa quello che vuoi.

Se cerchi solo uno strumento districante, che ad esempio potresti usare per manipolare le cose mostrate in un file di log, dai un'occhiata a c++filt, che viene fornito con binutils. Può separare i nomi dei simboli C ++ e Java.


Solo per notare, sia cxa_demange () (che il codice collegato a usa) che cx ++ filt sono specifici di gcc. Non esiste un modo portatile per farlo.
KeithB

c ++ filt non lo taglia, ho bisogno di questa roba (o la maggior parte di essa) in fase di compilazione, per lo più fatta con le macro.
capolinea

4
Il collegamento a type_strings.cpp sembra interrotto.
StackedCrooked

1
Ciao @GregoryPakosz Anche il link github nel tuo commento sopra sembra rotto :( Cheers
olibre

Solo un importante FYI: abi::__cxa_demangle()e il suo genere <cxxabi.h> non è specifico del GCC - potrebbero essere stati solo GCC in un lontano passato, ma al momento della scrittura di questo articolo, <cxxabi.h>era uno standard ad-hoc radicato. Quindi, mentre il link del codice della risposta era DOI, posso garantire che Clang abbia fornito supporto di prima classe in questo caso ... qv, dalla libcxxabifonte di Clang : il rispettivo decl, impl, enorme test: git.io/vRTBo , git.io/vRTBh , git.io/vRTRf - i commenti del codice di test indicano che l'implementazione di Clang è in grado di districare di più , in qualche modo, rispetto a GCC.
fish2000

4

È definita l'implementazione, quindi non è qualcosa che sarà portatile. In MSVC ++, name () è il nome non decorato, e devi guardare raw_name () per ottenere quello decorato.
Solo una pugnalata nel buio qui, ma sotto gcc, potresti voler dare un'occhiata a demangle.h


3

Non è una soluzione completa, ma potresti voler guardare cosa definiscono alcune delle macro standard (o ampiamente supportate). È comune nel codice di registrazione vedere l'uso delle macro:

__FUNCTION__
__FILE__
__LINE__

e.g.:

log(__FILE__, __LINE__, __FUNCTION__, mymessage);

4
Per non parlare di PRETTY_FUNCTION .
CesarB

1
Questo ti darà le informazioni su dove ti trovi nel codice. Quello che la domanda stava chiedendo era un bel nome di un tipo, come std :: vector.
KeithB

Ha detto che era per il debug e ho affermato che non era una soluzione completa. Altre macro come FUNCDNAME restituiranno il nome decorato.
Luca

In realtà, rileggendo la domanda, era "Attualmente sto lavorando su un codice di registrazione che dovrebbe - tra le altre cose - stampare informazioni sulla funzione chiamante". Funziona.
Max Lybbert

Non è completo, poiché non conosco lo spazio dei nomi. Questo è già nel mio codice. Ma grazie comunque.
capolinea

3

Ho anche trovato una macro chiamata __PRETTY_FUNCTION__, che fa il trucco. Dà un bel nome alla funzione (figure :)). Questo è ciò di cui avevo bisogno.

Cioè mi dà quanto segue:

virtual bool mutex::do_unlock()

Ma non credo che funzioni su altri compilatori.


Sì, PRETTY_FUNCTION è specifico per gcc.
Greg Rogers,

2

Una leggera variazione sulla soluzione di Ali. Se vuoi che il codice sia ancora molto simile a

typeid(bla).name(),

scrivendo questo invece

Typeid(bla).name() (diverso solo per la prima lettera maiuscola)

allora potresti essere interessato a questo:

Nel file type.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 rimane lo stesso della soluzione di Ali


1

Dai un'occhiata a quello __cxa_demangleche puoi trovare cxxabi.h.


Ho preso, è deprecato, secondo il messaggio che ricevo.
capolinea

Dove hai trovato quel messaggio? L'ho appena cercato su Google e sembra essere supportato, nessuna prova di essere deprecato.
Ali

Forse è deprecato in :: namespace. Usa abi :: __ cxa_demangle e non riceverai alcun avviso. Che gcc stai usando?
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
AVVERTIMENTO! Il buffer deve essere allocato da malloc o specificato come NULL. NON allocarlo in pila. Vedi il mio codice qui sotto.
Ali

1

La soluzione accettata [1] funziona per lo più bene. Ho trovato almeno un caso (e non lo definirei un caso d'angolo) in cui non riporta quello che mi aspettavo ... con riferimenti.

Per quei casi ho trovato un'altra soluzione, pubblicata in fondo.

Caso problematico (utilizzando typecome definito in [1]):

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

produce

Type of i is int
Type of ri is int

Soluzione (utilizzando type_name<decltype(obj)>(), vedere il codice di seguito):

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

produce

Type of i is int
Type of ri is int&

come desiderato (almeno da me)

Codice . Deve essere in un'intestazione inclusa, non in una fonte compilata separatamente, a causa di problemi di specializzazione. Vedere riferimento undefined alla funzione template per esempio.

#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

Ho sempre desiderato utilizzare type_info, ma sono sicuro che il risultato della funzione membro name () non è standard e non restituirà necessariamente nulla che possa essere convertito in un risultato significativo.
Se ti attieni a un compilatore, forse esiste una funzione specifica del compilatore che farà quello che vuoi. Controlla la documentazione.


0

Seguendo la soluzione di Ali, ecco l' alternativa basata su modelli C ++ 11 che ha funzionato meglio per il mio utilizzo.

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

Utilizzo:

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

Stamperà:

double                                                                        
int                                                                           
test::SomeStruct
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.