Разрыв результата std:: type_info:: name

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

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

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

Ответ 1

Учитывая внимание, которое этот вопрос/ответ получает, и ценные отзывы от GManNickG, я немного очистил код. Даны две версии: одна с функциями С++ 11 и другая с только функциями С++ 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 (требуется С++ 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: Base*
Тип получателя: Derived

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

Если вы не можете использовать возможности С++ 11, вот как это можно сделать в С++ 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() прошел успешно, возвращает указатель на локальный стек выделенный массив... ouch!
Также обратите внимание, что если вы предоставляете буфер, abi::__cxa_demangle() предполагает, что он будет выделен в куче. Выделение буфера в стеке является ошибкой (из документа gnu): "Если output_buffer недостаточно длинный, он расширяется с помощью realloc". Вызов realloc() указателя на стек... ouch! (См. Также Игорь Скочинский добрый комментарий.)

Вы можете легко проверить обе эти ошибки: просто уменьшите размер буфера в принятом ответе (с 7 сентября 2013 г.) от 1024 до чего-то меньшего, например 16, и дайте ему что-то с именем не более 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;
}

Ответ 2

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, как было предложено ранее.

Ответ 3

Это то, что мы используем. 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  

Ответ 4

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

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

Ответ 5

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

__FUNCTION__
__FILE__
__LINE__

e.g.:

log(__FILE__, __LINE__, __FUNCTION__, mymessage);

Ответ 6

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

Ответ 7

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

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

virtual bool mutex::do_unlock()

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

Ответ 8

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

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

Ответ 9

Взгляните на __cxa_demangle, который вы можете найти в cxxabi.h.

Ответ 10

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

Ответ 11

Я всегда хотел использовать type_info, но я уверен, что результат функции-члена name() нестандартен и не обязательно будет возвращать все, что может быть преобразовано в значимый результат.
Если вы придерживаетесь одного компилятора, возможно, какая-то функция компилятора, которая будет делать то, что вы хотите. Проверьте документацию.

Ответ 12

Принятое решение [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;
}