Розв’язування результату std :: type_info :: name


93

Зараз я працюю над кодом реєстрації, який повинен, серед іншого, друкувати інформацію про функцію виклику. Це має бути відносно просто, у стандартних C ++ є type_infoклас. Він містить назву typeid'd класу / функції / тощо. але це збито. Це не дуже корисно. Тобто typeid(std::vector<int>).name()повертається St6vectorIiSaIiEE.

Чи є спосіб зробити щось корисне з цього? Як std::vector<int>і у наведеному вище прикладі. Якщо він працює лише для нешаблонних класів, це теж добре.

Рішення повинно працювати для gcc, але було б краще, якби я міг його портувати. Це для ведення журналу, тому це не так важливо, що його не можна вимкнути, але він повинен бути корисним для налагодження.

Відповіді:


117

Зважаючи на увагу, яке отримує це питання / відповідь, і цінні відгуки від 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: Base*
Тип пункту:Derived

Тестується з g ++ 4.7.2, g ++ 4.9.0 20140302 (експериментальний), clang ++ 3.4 (trunk 184647), clang 3.5 (trunk 202594) на Linux 64 bit та 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 doc): "Якщо 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;
}

Зверніть увагу, що це потрібно #include <cxxabi.h>. В іншому випадку працювали чудово, спасибі.
jterrace

2
З Документів : output_bufferобласть пам'яті, виділену з malloc, * довжиною в байтах, в яку зберігається розменене ім'я. Якщо output_buffer недостатньо довгий, він розширюється за допомогою realloc. output_buffer може натомість мати значення NULL; у такому випадку виведене ім'я розміщується в області пам'яті, виділеній malloc.
Ігор Скочинський

2
@IgorSkochinsky Так, у моєму попередньому коментарі є помилка, але я не можу це редагувати. Що я хотів написати: "Востаннє я перевіряв, чи abi::__cxa_demangleочікував, що він буде виділений на купі ". Дякую вам за пошук документа!
Алі

1
Зверніть увагу, що технічно це може витікати, якщо ret_valкине під час будівництва. Ви можете використовувати охорону діапазону для захисту від цього.
GManNickG

3
Якщо це, мабуть, було б більш зрозумілим для використання std::unique_ptr<char, decltype(&std::free)>як підпис для вашого вказівника.
mindvirus

27

Ядро Boost містить деманглера. Checkout 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, як було запропоновано раніше.


1
Якщо підсилення є варіантом, це найкращий спосіб!
hbobenicio

13

Це те, що ми використовуємо. 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  

6
Вам потрібно включити #include <cxxabi.h>.
fuenfundachtzig

Цікаво. У мене визначено __cxa_demangle без HAVE_CXA_DEMANGLE
mkb

@Matt Що я хотів сказати, це те, що наша система побудови, заснована на autoconf, встановлює лише HAVE_CXA_DEMANGLE, якщо вона є в наявності.
KeithB

19
УВАГА! Вищевказаний код, ймовірно, призведе до збою програми. Буфер повинен бути або виділений malloc, або вказаний як NULL. НЕ виділяйте його на стек. Дивіться мій код нижче.
Алі

дивись, рес може повернутись
НУЛЬ

8

Ось подивіться на type_strings.hpp, він містить функцію, яка робить те, що ви хочете.

Якщо ви просто шукаєте інструмент для демонтажу, який ви, наприклад, можете використати для маніпулювання речей, показаних у файлі журналу, подивіться c++filt, який постачається з бінутами. Він може демонтувати C ++ та імена символів Java.


Зауважимо, як cxa_demange () (який код пов'язаний з використанням), так і cx ++ filt є специфічними для gcc. Немає портативного способу зробити це.
KeithB

c ++ filt не вирізає, мені потрібні ці речі (або більшість із них) під час компіляції, в основному це робиться з макросами.
термін

4
Посилання на type_strings.cpp здається порушеним.
StackedCrooked

1
Привіт @GregoryPakosz Посилання github у твоєму вище коментарі теж здається порушеним :(
Привіт

Просто важливий FYI: abi::__cxa_demangle()і подібні до <cxxabi.h> нього не є специфічними для GCC - вони, можливо, були лише GCC у далекому минулому, але на момент написання цього допису це <cxxabi.h>був закріплений спеціальний стандарт. Тому, хоча в кодовому посиланні відповіді було DOI, я можу поручитися за надання Кланг надання першокласної підтримки в цьому випадку ... qv, від libcxxabiджерела Clang : відповідний decl, impl, величезний тест: git.io/vRTBo , git.io/vRTBh , git.io/vRTRf - коментарі тестового коду відзначають, що реалізація Clang є здатною до дедалі більше, ніж GCC.
fish2000

4

Його реалізація визначена, тому це не те, що буде портативним. У MSVC ++ ім'я () - це незадеклароване ім'я, і ​​вам потрібно подивитися на raw_name (), щоб отримати прикрашене.
Тут просто колоти в темряві, але під gcc ви можете подивитися на demangle.h


3

Це не повне рішення, але ви можете поглянути на те, що визначають деякі зі стандартних (або широко підтримуваних) макросів. У коді реєстрації звичайно бачити використання макросів:

__FUNCTION__
__FILE__
__LINE__

e.g.:

log(__FILE__, __LINE__, __FUNCTION__, mymessage);

4
Не кажучи вже про PRETTY_FUNCTION .
CesarB

1
Це дасть вам інформацію про те, де ви перебуваєте в коді. Запитання, яке задавали, це гарне ім’я типу, як std :: vector.
KeithB

Він згадав, що це для налагодження, і я заявив, що це не повне рішення. Інші макроси, такі як FUNCDNAME , повернуть оформлене ім'я.
luke

Насправді, перечитавши питання, було "Я зараз працюю над деяким кодом реєстрації, який повинен, серед іншого, друкувати інформацію про функцію виклику". Це працює.
Макс Лібберт

Це не повно, оскільки я не знаю простір імен. Це вже в моєму коді. Але все одно дякую.
кінцева

3

Я також знайшов макрос __PRETTY_FUNCTION__, який називається , що робить трюк. Це дає гарне ім'я функції (цифри :)). Це те, що мені потрібно було.

Тобто це дає мені таке:

virtual bool mutex::do_unlock()

Але я не думаю, що це працює на інших компіляторах.


Так, PRETTY_FUNCTION - специфічний для gcc.
Грег Роджерс

2

Невелика варіація рішення Алі. Якщо ви хочете, щоб код все ще був дуже схожий на

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 залишається таким же, як у рішенні Ali


1

Погляньте, __cxa_demangleщо ви можете знайти на cxxabi.h.


Я взяв, це застаріло, відповідно до повідомлення, яке я отримую.
кінцева

Де ви знайшли це повідомлення? Я просто погуглив це, і, здається, його підтримують, жодних доказів того, що його застаріли.
Алі

Можливо, це застаріло в :: просторі імен. Використовуйте abi :: __ cxa_demangle, і ви не отримаєте попередження. Який gcc ви використовуєте?
onitake

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

11
УВАГА! Буфер повинен бути або виділений malloc, або вказаний як NULL. НЕ виділяйте його на стек. Дивіться мій код нижче.
Алі

1

Загальноприйнята рішення [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;
}

0

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


0

Після рішення Алі, ось шаблонна альтернатива C ++ 11, яка найкраще працювала для мого використання.

// type.h
#include <cstdlib>
#include <memory>
#include <cxxabi.h>

template <typename T>
std::string demangle() {
  int status = -4;

  std::unique_ptr<char, void (*)(void*)> res{
      abi::__cxa_demangle(typeid(T).name(), NULL, NULL, &status), std::free};
  return (status == 0) ? res.get() : typeid(T).name();
}

Використання:

// main.cpp
#include <iostream>

namespace test {
    struct SomeStruct {};
}

int main()
{
    std::cout << demangle<double>() << std::endl;
    std::cout << demangle<const int&>() << std::endl;
    std::cout << demangle<test::SomeStruct>() << std::endl;

    return 0;
}

Буде надруковано:

double                                                                        
int                                                                           
test::SomeStruct
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.