Domanda

Attualmente sto lavorando ad un codice di registrazione che dovrebbe, tra l'altro, stampare informazioni sulla funzione di chiamata. Questo dovrebbe essere relativamente semplice, lo standard C ++ ha una classe type_info. Questo contiene il nome della classe / funzione / etc typeid'd. ma è rovinato. Non è molto utile Cioè typeid(std::vector<int>).name() restituisce 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 lo stesso.

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

È stato utile?

Soluzione

Data l'attenzione che questa domanda / risposta riceve 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 si può fare 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 (dal 7 settembre 2013) , quando la chiamata a abi::__cxa_demangle() ha esito positivo, < strong> restituisce un puntatore a un array locale allocato in stack ... ahi!
Si noti inoltre che se si fornisce un buffer, output_buffer presuppone che sia allocato sull'heap. Allocare il buffer nello stack è un bug (dal documento gnu): & Quot; Se realloc non è abbastanza lungo, viene espanso usando realloc(). & Quot; Chiamare HAVE_CXA_DEMANGLE su un puntatore allo stack ... ahi! (Vedi anche il commento gentile di Igor Skochinsky .)

Puoi facilmente verificare entrambi questi bug: riduci la dimensione del buffer nella risposta accettata (dal 7 settembre 2013) da 1024 a qualcosa di più piccolo, ad esempio 16, e assegnagli qualcosa con un nome no più lungo di 15 (quindi __GNUG__ non è non chiamato). Tuttavia, a seconda del sistema e delle ottimizzazioni del compilatore, l'output sarà: crash immondizia / nulla / programma.
Per verificare il secondo bug: impostare la dimensione del buffer su 1 e chiamarlo con qualcosa il cui nome è più lungo di 1 carattere. Quando lo esegui, il programma si blocca quasi sicuramente mentre tenta di chiamare <=> con un puntatore allo stack.


(La vecchia risposta del 27 dic 2010)

Importanti modifiche apportate a codice KeithB : il buffer deve essere allocato da Malloc o specificato come NULL . NON allocare in pila.

È consigliabile controllare anche quello stato.

Non sono riuscito a trovare <=>. Controllo <=> sebbene ciò non garantisca nemmeno la compilazione del codice. 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;
}

Altri suggerimenti

Boost core contiene un demangler. Acquista 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.

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  

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

Se cerchi solo uno strumento di demangling, che ad es. potrebbe usare per manipolare cose mostrate in un file di registro, dare un'occhiata a c++filt, che viene fornito con binutils. Può districare i nomi dei simboli C ++ e Java.

Non è una soluzione completa, ma potresti voler esaminare ciò che definiscono alcune 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);

È 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 al buio qui, ma sotto gcc, potresti voler guardare demangle.h

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

vale a dire. mi dà quanto segue:

virtual bool mutex::do_unlock()

Ma non penso che funzioni su altri compilatori.

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

typeid(bla).name(),

scrivendo invece

Typeid(bla).name() (diverso solo dalla 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 Ali

Dai un'occhiata a __cxa_demangle che puoi trovare su cxxabi.h.

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

La soluzione accettata [1] funziona principalmente bene. Ho trovato almeno un caso (e non lo definirei un caso angolare) in cui non riporta ciò che mi aspettavo ... con riferimenti.

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

Caso problematico (utilizzando type come 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 (usando type_name<decltype(obj)>(), vedi il codice sotto):

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 trovarsi in un'intestazione inclusa, non in una fonte compilata separatamente, a causa di problemi di specializzazione. Vedi riferimento indefinito 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;
}

Ho sempre desiderato usare type_info, ma sono sicuro che il risultato della funzione membro name () non è standard e non restituirà necessariamente tutto ciò che può essere convertito in un risultato significativo.
Se stai rispettando un solo compilatore, potrebbe esserci una funzione specifica del compilatore che farà ciò che desideri. Controlla la documentazione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top