Pergunta

Atualmente estou trabalhando em algum código de registro que deveria - entre outras coisas - imprimir informações sobre a função de chamada. Este deve ser relativamente fácil, C ++ padrão tem uma classe type_info. Este contém o nome da classe typeid'd / função / etc. mas é mutilado. Não é muito útil. Ou seja, typeid(std::vector<int>).name() retornos St6vectorIiSaIiEE.

Existe uma maneira de produzir algo útil com isso? Como std::vector<int> para o exemplo acima. Se ele só funciona para classes não-modelo, tudo bem também.

A solução deve funcionar para gcc, mas seria melhor se eu pudesse transportá-lo. É para a exploração madeireira por isso não é tão importante que não pode ser desligado, mas deve ser útil para depurar.

Foi útil?

Solução

Dada a atenção a esta pergunta / resposta recebe, eo feedback valioso de GManNickG , eu ter limpado o código um pouco. Duas versões são dadas:. Um com C ++ 11 características e outra com apenas C ++ 98 recursos

No arquivo 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

No arquivo type.cpp (requer 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;
}

Ela imprime:

Tipo de ptr_base: Base*
Tipo de pointee: Derived

Testado com g ++ 4.7.2, 4.9.0 20140302 g ++ (experimental), tinido ++ 3,4 (tronco 184647), tinido 3,5 (tronco 202594) em Linux 64 bits e g ++ 4.7.2 (Mingw32, Win32 XP SP2).

Se você não pode usar C ++ 11 características, aqui está como isso pode ser feito em C ++ 98, o arquivo type.cpp é agora:

#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


(Actualização a partir de 8 de setembro de 2013)

a resposta aceita (a partir de 07 setembro de 2013) , quando a chamada para abi::__cxa_demangle() for bem sucedida, retorna um ponteiro para um, pilha matriz alocada locais ...! ouch
Observe também que se você fornecer um buffer, abi::__cxa_demangle() assume que ele seja alocado no heap. Alocar o buffer na pilha é um bug (do doc gnu): "Se output_buffer não é suficientemente longo, ele é expandido usando realloc." Chamando realloc() em um ponteiro para a pilha ... ouch! (Veja também Igor Skochinsky 's tipo comentários.)

Você pode facilmente verificar esses dois erros: apenas reduzir o tamanho do buffer na resposta aceita (a partir de 07 de setembro de 2013) de 1024 para algo menor, por exemplo 16, e dar-lhe algo com um nome não mais de 15 (assim realloc() é não chamado). Ainda assim, dependendo do seu sistema e as otimizações do compilador, a saída será: lixo / nada / crash programa
. Para verificar o segundo bug: definir o tamanho do buffer para 1 e chamá-lo com algo cujo nome tem mais de 1 personagem. Ao executá-lo, o programa trava quase certamente como ele tenta chamar realloc() com um ponteiro para a pilha.


(A velha resposta de 27 de dezembro de 2010)

mudanças importantes feitas de KeithB código : o tampão tem de ser tanto alocada por malloc ou especificado como NULL . não alocá-lo na pilha.

É aconselhável verificar esse status também.

Eu não conseguiram encontrar HAVE_CXA_DEMANGLE. Eu verifico __GNUG__ embora isso não garante que o código vai mesmo compilar. Alguém tem uma idéia melhor?

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

Outras dicas

núcleo impulso contém uma demangler. Caixa 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>
}

É basicamente apenas um wrapper para abi::__cxa_demangle, como tem sido sugerido anteriormente.

Este é o que usamos. HAVE_CXA_DEMANGLE só é definido se disponíveis (as versões recentes do GCC apenas).

#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  

Aqui, dê uma olhada type_strings.hpp ele contém uma função que faz o que quiser.

Se você simplesmente olhar para uma ferramenta desembaralhamento, que por exemplo, poderia usar para coisas mangle mostrado em um arquivo de log, dê uma olhada c++filt, que vem com binutils. Pode demangle nomes de símbolos C ++ e Java.

Nem uma solução completa, mas você pode querer olhar para o que alguns da norma (ou amplamente suportada) da macro definir. É comum em código de registro para ver o uso das macros:

__FUNCTION__
__FILE__
__LINE__

e.g.:

log(__FILE__, __LINE__, __FUNCTION__, mymessage);

É de implementação definido, por isso não é algo que vai ser portátil. Em MSVC ++, nome () é o nome undecorated, e você tem que olhar para RAW_NAME () para obter o decorados.
Apenas uma facada no escuro aqui, mas sob gcc, você pode querer olhar em demangle.h

Eu também achei uma macro chamada __PRETTY_FUNCTION__, que faz o truque. Dá um nome de função muito (figuras :)). Isto é o que eu precisava.

i. dá-me o seguinte:

virtual bool mutex::do_unlock()

Mas eu não acho que ele funciona em outros compiladores.

Uma pequena variação na solução de Ali. Se você deseja que o código de ainda ser muito semelhante ao

typeid(bla).name(),

escrevendo isso em vez

Typeid(bla).name() (diferindo apenas na capital de primeira letra)

então você pode estar interessado neste:

No arquivo 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 estadias mesmo que em solução de Ali

Dê uma olhada __cxa_demangle que você pode encontrar em 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;
  }

O aceito solução [1] trabalha principalmente bem. Eu encontrei pelo menos um caso (e eu não chamaria isso de um caso de canto) onde não denunciar o que eu esperava ... com referências.

Para esses casos, eu encontrei outra solução, afixado na parte inferior.

caso problemático (usando type como definido em [1]):

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

produz

Type of i is int
Type of ri is int

Solução (usando type_name<decltype(obj)>(), ver código abaixo):

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

produz

Type of i is int
Type of ri is int&

como desejado (pelo menos por mim)

Código . Tem que ser em um cabeçalho incluído, não em uma fonte compilados separadamente, devido a questões de especialização. Consulte para função de modelo, por exemplo.

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

Eu sempre quis usar type_info, mas tenho certeza de que o resultado do nome da função membro () é não-padrão e não necessariamente devolver qualquer coisa que pode ser convertido para um resultado significativo.
Se você está aderindo a um compilador, há talvez uma função específica compilador que vai fazer o que quiser. Verifique a documentação.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top