Pregunta

Actualmente estoy trabajando en un código de registro que supone, 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. Contiene el nombre de la clase / función typeid'd / etc. Pero está destrozado. No es muy útil Es decir. typeid(std::vector<int>).name() devuelve St6vectorIiSaIiEE.

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

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.

¿Fue útil?

Solución

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

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

#endif

En el 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 propietario: Derived

Probado con g ++ 4.7.2, g ++ 4.9.0 20140302 (experimental), clang ++ 3.4 (trunk 184647), clang 3.5 (trunk 202594) en Linux 64 bit y g ++ 4.7.2 (Mingw32, Win32 XP SP2).

Si no puede usar las características de C ++ 11, así es como se puede hacer en C ++ 98, el archivo type.cpp ahora es:

#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 de septiembre de 2013)

La respuesta aceptada (a partir del 7 de septiembre de 2013) , cuando la llamada a abi::__cxa_demangle() es exitosa, < strong> devuelve un puntero a una matriz local asignada a la pila ... ¡ay!
También tenga en cuenta que si proporciona un búfer, output_buffer supone que se asignará en el montón. La asignación del búfer en la pila es un error (del documento gnu): & Quot; Si realloc no es lo suficientemente largo, se expande usando realloc(). & Quot; Llamando HAVE_CXA_DEMANGLE con un puntero a la pila ... ¡ay! (Véase 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 dele algo con un nombre no más de 15 (por lo que __GNUG__ no se llama ). 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: establezca el tamaño del búfer en 1 y llámelo con algo cuyo nombre tenga más de 1 carácter. Cuando lo ejecuta, el programa casi con seguridad falla al intentar llamar a <=> con un puntero a la pila.


(La respuesta anterior del 27 de diciembre de 2010)

Cambios importantes realizados en Código de KeithB : el búfer debe ser asignado por malloc o especificado como NULL . NO lo asigne en la pila.

También es aconsejable verificar ese estado.

No pude encontrar <=>. Verifico <=> aunque eso no garantiza que el código incluso se compilará. ¿Alguien tiene una idea mejor?

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

Otros consejos

El núcleo de refuerzo contiene un demandante. Pago 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>
}

Básicamente es solo un contenedor para abi::__cxa_demangle, como se sugirió anteriormente.

Esto es lo que usamos. HAVE_CXA_DEMANGLE solo se establece si está disponible (solo versiones recientes de 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  

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

Si solo busca una herramienta exigente, que p. podría utilizar para destrozar 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.

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

__FUNCTION__
__FILE__
__LINE__

e.g.:

log(__FILE__, __LINE__, __FUNCTION__, mymessage);

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

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.

Una ligera variación en la solución de Ali. Si desea que el código siga siendo muy similar a

typeid(bla).name(),

escribiendo esto en su lugar

Typeid(bla).name() (que difiere solo en la primera letra mayúscula)

entonces te puede interesar esto:

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

Eche un vistazo a __cxa_demangle que puede encontrar en 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 solución aceptada [1] funciona principalmente bien. Encontré al menos un caso (y no lo llamaría un caso de esquina) donde no informa lo que esperaba ... con referencias.

Para esos casos, encontré otra solución, publicada en la parte inferior.

Caso problemático (usando type como se define en [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

Solución (usando type_name<decltype(obj)>(), vea el código a continuación):

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&

según lo deseado (al menos por mí)

Código . Tiene que estar en un encabezado incluido, no en una fuente compilada por separado, debido a problemas de especialización. Consulte referencia indefinida a la función de plantilla , por ejemplo.

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

Siempre quise usar type_info, pero estoy seguro de que el resultado de la función de miembro name () no es estándar y no necesariamente devolverá nada que pueda convertirse en un resultado significativo.
Si se apega a un compilador, puede haber una función específica del compilador que haga lo que desee. Consulte la documentación.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top