Зважаючи на увагу, яке отримує це питання / відповідь, і цінні відгуки від 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>
. В іншому випадку працювали чудово, спасибі.