2008-11-11 5 views
69

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

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

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

ответ

86

Учитывая внимание на этот вопрос/ответ получает, и ценную обратную связь от 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 

В файле 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*
Тип pointee: Derived

Испытано с г ++ 4.7.2, G ++ 4.9.0 20140302 (экспериментальный), лязг ++ 3.4 (ствол 184647), лязг 3.5 (ствол 202594) на Linux 64 бит и 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)

The accepted answer (as of Sep 7, 2013), когда вызов abi::__cxa_demangle() успешно, возвращает указатель на локальный массив, выделенный стекем ... ouch!
Также обратите внимание, что если вы предоставляете буфер, abi::__cxa_demangle() предполагает, что он будет выделен на кучу. Выделение буфера в стеке является ошибкой (из документа gnu): «Если output_buffer недостаточно длинный, он расширяется, используя realloc».Вызов realloc() по указателю на стек ... ouch! (См. Также Igor Skochinsky 's kind comment.)

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


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

Важных изменений, внесенных в KeithB's code: буфер должен быть либо выделен таНосом или указан как 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; 
} 
4

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

0

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

1

Посмотрите на __cxa_demangle, который вы можете найти на странице cxxabi.h.

+0

Я взял, это устарело, в соответствии с сообщением, которое я получаю. – terminus 2008-11-11 20:10:40

+0

Где вы нашли это сообщение? Я просто искал его и, похоже, поддерживал его, никаких доказательств того, что он устарел. – Ali 2010-12-27 20:22:09

+0

Возможно, это устарело в :: пространстве имен. Используйте abi :: __ cxa_demangle, и вы не получите предупреждение. Какой gcc вы используете? – onitake 2011-07-29 01:03:33

14

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

Вам нужно включить `#include `. – fuenfundachtzig 2010-01-20 18:37:48

+0

Интересно. У меня есть __cxa_demangle без определения HAVE_CXA_DEMANGLE – mkb 2010-11-10 20:52:56

+0

@Matt. Я хотел сказать, что наша система сборки, основанная на autoconf, только устанавливает HAVE_CXA_DEMANGLE, если она доступна. – KeithB 2010-11-10 22:11:04

5

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

__FUNCTION__ 
__FILE__ 
__LINE__ 

e.g.: 

log(__FILE__, __LINE__, __FUNCTION__, mymessage); 
8

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

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

3

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

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

virtual bool mutex::do_unlock() 

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

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

10

ядро ​​подталкивания содержит demangler. Заказ 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, как было предложено ранее.