2008-11-11 16 views
69

Actualmente estoy trabajando en un código de registro que suponía, entre otras cosas, imprimir información sobre la función de llamada. Esto debería ser relativamente fácil, C++ estándar tiene una clase type_info. Este contiene el nombre de la clase/función/etc typeid. pero está destrozado. No es muy útil. Es decir. typeid(std::vector<int>).name() devuelve St6vectorIiSaIiEE.Desenrollando el resultado de std :: type_info :: name

¿Hay alguna forma de producir algo útil a partir de esto? Como std::vector<int> para el ejemplo anterior. Si solo funciona para clases que no son de plantilla, está bien también.

La solución debería funcionar para gcc, pero sería mejor si pudiera portarla. Es para iniciar sesión, por lo que no es tan importante que no se pueda desactivar, pero debería ser útil para la depuración.

Respuesta

86

Dada la atención esta pregunta/respuesta recibe, y la retroalimentación valiosa de GManNickG, he limpiado el código un poco. Se dan dos versiones: una con características C++ 11 y otra con solo características C++ 98.

En el archivo tipo.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 

En archivo type.cpp (requiere 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 

Uso:

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

Imprime:

Tipo de ptr_base: Base*
Tipo de pointee: Derived

probado con g ++ 4.7.2, g ++ 4.9.0 20140302 (experimental), sonido metálico ++ 3,4 (tronco 184 647), cling 3,5 (tronco 202.594) en Linux 64 bits y g ++ 4.7.2 (Mingw32, Win32 XP SP2).

Si no puede utilizar C++ 11 características, aquí es cómo se puede hacer en C++ 98, el archivo type.cpp es ahora:

#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 


(actualización del 8 Sep, 2013)

The accepted answer (as of Sep 7, 2013), cuando la llamada a abi::__cxa_demangle() tiene éxito, devuelve un puntero a una matriz asignada de pila local ... ¡ay!
También tenga en cuenta que si proporciona un búfer, abi::__cxa_demangle() asume que debe asignarse en el montón. La asignación del búfer en la pila es un error (del doc. Gnu): "Si output_buffer no es lo suficientemente largo, se expande usando realloc."Llamando realloc() en un puntero a la pila ... ¡ay! (Consulte también el comentario amable de Igor Skochinsky.)

Puede verificar fácilmente estos dos errores: simplemente reduzca el tamaño del búfer en la respuesta aceptada (a partir del 7 de septiembre de 2013) de 1024 a algo más pequeño, por ejemplo 16, y darle algo con un nombre no más largo que 15 (entonces realloc() es no llamado llamado). Aún así, dependiendo de su sistema y las optimizaciones del compilador, el resultado será: basura/nada/bloqueo del programa.
Para verificar el segundo error: configure el tamaño del búfer en 1 y llámelo con algo cuyo nombre sea más largo que 1 carácter. Cuando lo ejecuta, el programa casi con seguridad se bloquea cuando intenta llamar al realloc() con un puntero a la pila.


(La vieja respuesta del 27 Dic-2010)

cambios importantes realizados en KeithB's code: el buffer tiene que ser ya sea asignado por malloc o especificados como NULL. NO lo asigne en la pila.

Es sabio verificar ese estado también.

No pude encontrar HAVE_CXA_DEMANGLE. Reviso __GNUG__ aunque eso no garantiza que el código siquiera se compile. Alguien tiene una mejor idea?

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

Tenga en cuenta que esto necesita '' #include ''. De lo contrario, funcionó muy bien, gracias. – jterrace

+0

Ooops, lo siento. ¡Reparado y gracias! – Ali

+0

¿Puede explicar por qué no se puede asignar el búfer en la pila? Porque hasta ahora funcionó bien para mí. – terminus

4

Su implementación está definida, por lo que no es algo portátil. En MSVC++, name() es el nombre no decorado, y debe buscar en raw_name() para obtener el decorado.
Sólo una puñalada en la oscuridad aquí, pero bajo gcc, es posible que desee ver en demangle.h

0

Siempre he querido utilizar type_info, pero estoy seguro de que el resultado de la función de Identidades() es no estándar y no necesariamente devolverá nada que pueda convertirse en un resultado significativo.
Si se apega a un compilador, tal vez haya una función específica del compilador que hará lo que usted desee. Verifique la documentación.

1

Eche un vistazo a __cxa_demangle que se puede encontrar en cxxabi.h.

+0

Tomé, está en desuso, de acuerdo con el mensaje que recibo. – terminus

+0

¿Dónde encontraste ese mensaje? Acabo de buscar en Google y parece ser compatible, no hay evidencia de que esté en desuso. – Ali

+0

Tal vez está en desuso en :: namespace. Use abi :: __ cxa_demangle y no recibirá una advertencia. ¿Qué gcc estás usando? – onitake

14

Esto es lo que usamos. HAVE_CXA_DEMANGLE solo se establece si está disponible (versiones recientes de GCC solamente).

#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 
+5

Debe incluir '#include '. – fuenfundachtzig

+0

Interesante. Tengo __cxa_demangle sin HAVE_CXA_DEMANGLE definido – mkb

+0

@Matt Lo que quise decir es que nuestro sistema de compilación, basado en autoconf, solo establece HAVE_CXA_DEMANGLE si está disponible. – KeithB

5

No es una solución completa, pero es posible que desee ver lo que definen algunos de los macro estándar (o ampliamente compatibles). Es común en código de registro para ver el uso de las macros:

__FUNCTION__ 
__FILE__ 
__LINE__ 

e.g.: 

log(__FILE__, __LINE__, __FUNCTION__, mymessage); 
+3

Sin mencionar __PRETTY_FUNCTION__. – CesarB

+0

Esto le dará la información sobre dónde se encuentra en el código. Lo que la pregunta estaba haciendo era un bonito nombre de tipo, como std :: vector. – KeithB

+0

Mencionó que era para la depuración, y dije que no era una solución completa. Otras macros como __FUNCDNAME__ devolverán el nombre decorado. – luke

8

Aquí, echar un vistazo a type_strings.hpp que contiene una función que hace lo que quiere.

Si solo busca una herramienta de demanda, que p. Ej. podría utilizar para manipular cosas que se muestran en un archivo de registro, eche un vistazo a c++filt, que viene con binutils. Puede exigir nombres de símbolos C++ y Java.

+0

Solo para tener en cuenta, tanto cxa_demange() (que el código vinculado a usos) y cx ++ filt son específicos de gcc. No hay una forma portátil de hacer esto. – KeithB

+0

C++ filt no lo corta, necesito esto (o la mayor parte) en tiempo de compilación, principalmente con macros. – terminus

+4

El enlace a type_strings.cpp parece estar roto. – StackedCrooked

3

También encontré una macro llamada __PRETTY_FUNCTION__, que hace el truco. Da un bonito nombre de función (figuras :)). Esto es lo que necesitaba.

I.e. me da lo siguiente:

virtual bool mutex::do_unlock() 

Pero no creo que funcione en otros compiladores.

+0

Sí, __PRETTY_FUNCTION__ es específico de gcc. –

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; 
    } 
+8

¡ADVERTENCIA! El buffer debe ser asignado por malloc o especificado como NULL. NO lo asigne en la pila. Ver mi código a continuación. – Ali

2

Una pequeña variación en la solución de Ali. Si desea que el código para seguir siendo muy similar a

typeid(bla).name(),

escribir este lugar

Typeid(bla).name() (sólo difieren en primera letra mayúscula)

entonces usted puede estar interesado en esto:

En archivo 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 estancias igual que en la solución de Ali

10

núcleo Boost contiene una demangler. Pedido 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> 
} 

Es básicamente un contenedor para abi::__cxa_demangle, como se ha sugerido anteriormente.

+1

Si aumentar es una opción, este es el mejor camino a seguir. – hbobenicio