Отмена результата std :: type_info :: name - PullRequest
80 голосов
/ 11 ноября 2008

В настоящее время я работаю над кодом регистрации, который, среди прочего, должен печатать информацию о вызывающей функции. Это должно быть относительно просто, стандартный C ++ имеет класс type_info. Он содержит имя класса / функции typeid'd и т. Д. но он покалечен Это не очень полезно. То есть typeid(std::vector<int>).name() возвращает St6vectorIiSaIiEE.

Есть ли способ извлечь из этого что-то полезное? Как std::vector<int> для приведенного выше примера. Если это работает только для не шаблонных классов, это тоже нормально.

Решение должно работать для gcc, но было бы лучше, если бы я мог его портировать. Это для ведения журнала, поэтому не так важно, чтобы его нельзя было отключить, но это должно быть полезно для отладки.

Ответы [ 12 ]

102 голосов
/ 27 декабря 2010

Учитывая внимание, которое получает этот вопрос / ответ, и ценный отзыв от GManNickG , я немного очистил код. Даны две версии: одна с функциями C ++ 11, а другая только с функциями C ++ 98.

В файле 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

В файле type.cpp (требуется 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

Использование:

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

Он печатает:

Тип базы данных ptr: Base*
Тип объекта: Derived

Протестировано с g ++ 4.7.2, g ++ 4.9.0 20140302 (экспериментальный), clang ++ 3.4 (транк 184647), clang 3.5 (транк 202594) в 64-битной Linux и g ++ 4.7.2 (Mingw32, Win32 XP SP2).

Если вы не можете использовать функции C ++ 11, вот как это можно сделать в C ++ 98, файл type.cpp теперь выглядит так:

#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


(обновление от 8 сентября 2013 г.)

Принятый ответ (по состоянию на 7 сентября 2013 г.) , если вызов abi::__cxa_demangle() успешен, возвращает указатель на локальный массив, выделенный стеком ... ой!
Также обратите внимание, что если вы предоставляете буфер, abi::__cxa_demangle() предполагает его размещение в куче. Выделение буфера в стеке является ошибкой (из документа gnu): "Если output_buffer недостаточно длинный, он расширяется с помощью realloc." Вызов realloc() на указатель на стек ... ой! (См. Также Игорь Скочинский добрый комментарий.)

Вы можете легко проверить обе эти ошибки: просто уменьшите размер буфера в принятом ответе (по состоянию на 7 сентября 2013 г.) с 1024 до чего-то меньшего, например, 16, и дайте ему что-то с именем not длиннее 15 (поэтому realloc() называется , а не вызывается). Тем не менее, в зависимости от вашей системы и оптимизации компилятора, вывод будет: мусор / ничего / сбой программы.
Чтобы проверить вторую ошибку: установите размер буфера равным 1 и вызовите его с именем, длина которого превышает 1 символ. Когда вы запускаете его, программа почти наверняка вылетает при попытке вызвать realloc() с указателем на стек.


(старый ответ от 27 декабря 2010 г.)

Важные изменения, внесенные в Код KeithB : буфер должен быть выделен malloc или задан как NULL. НЕ размещать его в стеке.

Целесообразно также проверить этот статус.

Мне не удалось найти HAVE_CXA_DEMANGLE. Я проверяю __GNUG__, хотя это не гарантирует, что код даже скомпилируется. У кого-нибудь есть идея получше?

#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;
}
20 голосов
/ 21 января 2016

Boost core содержит деманглер. Оформить заказ 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>
}

Это просто оболочка для abi::__cxa_demangle, как было предложено ранее.

11 голосов
/ 11 ноября 2008

Это то, что мы используем. HAVE_CXA_DEMANGLE устанавливается только при наличии (только последние версии 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  
8 голосов
/ 11 ноября 2008

Здесь, посмотрите на type_strings.hpp , она содержит функцию, которая делает то, что вы хотите.

Если вы просто ищете инструмент для разборки, который вы, например, можно использовать для искажения содержимого, отображаемого в файле журнала, взгляните на c++filt, который поставляется с binutils. Он может разбирать имена символов C ++ и Java.

5 голосов
/ 11 ноября 2008

Не полное решение, но вы можете посмотреть, что определяют некоторые из стандартных (или широко поддерживаемых) макросов. В журнале регистрации кода часто можно увидеть использование макросов:

__FUNCTION__
__FILE__
__LINE__

e.g.:

log(__FILE__, __LINE__, __FUNCTION__, mymessage);
4 голосов
/ 11 ноября 2008

Это определено реализацией, поэтому оно не будет переносимым. В MSVC ++ name () - это неокрашенное имя, и вы должны взглянуть на raw_name (), чтобы получить декорированное.
Просто удар в темноте здесь, но в gcc вы можете посмотреть на demangle.h

3 голосов
/ 11 ноября 2008

Я также нашел макрос с именем __PRETTY_FUNCTION__, который добился цели. Это дает красивое имя функции (цифры :)). Это то, что мне было нужно.

т.е. это дает мне следующее:

virtual bool mutex::do_unlock()

Но я не думаю, что это работает на других компиляторах.

2 голосов
/ 13 апреля 2015

Небольшое отклонение от решения Али. Если вы хотите, чтобы код все еще был очень похож на

typeid(bla).name(),

написание этого вместо

Typeid(bla).name() (отличается только заглавной первой буквой)

тогда вас может заинтересовать это:

В файле 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 остается таким же, как в решении Али

1 голос
/ 20 декабря 2018

принятое решение [1] работает в основном хорошо. Я нашел по крайней мере один случай (и я бы не назвал это угловым случаем), где он не сообщает, что я ожидал ... со ссылками.

Для этих случаев я нашел другое решение, размещенное внизу.

Проблемный случай (с использованием type, как определено в [1]):

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

производит

Type of i is int
Type of ri is int

Решение (с использованием type_name<decltype(obj)>(), см. Код ниже):

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

производит

Type of i is int
Type of ri is int&

по желанию (по крайней мере, мне)

Код , Это должно быть во включенном заголовке, а не в отдельно скомпилированном источнике, из-за проблем специализации. См., Например, неопределенную ссылку на шаблонную функцию .

#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;
}
1 голос
/ 12 апреля 2010
// 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;
  }
...