Вопрос

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

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

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

Это было полезно?

Решение

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

В файле тип.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

В файле тип.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: 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: файл тип.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, и дайте ему что-нибудь с именем нет дольше 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;
}

Другие советы

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

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

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

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

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

__FUNCTION__
__FILE__
__LINE__

e.g.:

log(__FILE__, __LINE__, __FUNCTION__, mymessage);

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

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

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

virtual bool mutex::do_unlock()

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

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

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

Взгляните на __cxa_demangle, который вы можете найти на 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;
  }

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top