Расшифровка результата std::type_info::name
-
07-07-2019 - |
Вопрос
В настоящее время я работаю над кодом регистрации, который должен, среди прочего, печатать информацию о вызывающей функции.Это должно быть относительно легко, стандартный 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 () нестандартен и не обязательно будет возвращать что-либо, что может быть преобразовано в значимый результат.
Если вы придерживаетесь одного компилятора, возможно, есть специальная функция компилятора, которая будет делать то, что вы хотите. Проверьте документацию.