Biorąc pod uwagę uwagę , jaką wzbudza to pytanie / odpowiedź, oraz cenne opinie od GManNickG , trochę wyczyściłem kod. Podano dwie wersje: jedną z funkcjami C ++ 11 i drugą z funkcjami C ++ 98.
W pliku 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
W pliku type.cpp (wymaga C ++ 11)
#include "type.hpp"
#ifdef __GNUG__
#include <cstdlib>
#include <memory>
#include <cxxabi.h>
std::string demangle(const char* name) {
int status = -4;
std::unique_ptr<char, void(*)(void*)> res {
abi::__cxa_demangle(name, NULL, NULL, &status),
std::free
};
return (status==0) ? res.get() : name ;
}
#else
std::string demangle(const char* name) {
return name;
}
#endif
Stosowanie:
#include <iostream>
#include "type.hpp"
struct Base { virtual ~Base() {} };
struct Derived : public Base { };
int main() {
Base* ptr_base = new Derived();
std::cout << "Type of ptr_base: " << type(ptr_base) << std::endl;
std::cout << "Type of pointee: " << type(*ptr_base) << std::endl;
delete ptr_base;
}
Drukuje:
Typ ptr_base: Base*
Typ pointee :Derived
Testowane z g ++ 4.7.2, g ++ 4.9.0 20140302 (eksperymentalne), clang ++ 3.4 (trunk 184647), clang 3.5 (trunk 202594) w systemie Linux 64-bit oraz g ++ 4.7.2 (Mingw32, Win32 XP SP2).
Jeśli nie możesz korzystać z funkcji C ++ 11, oto jak można to zrobić w C ++ 98, plik type.cpp ma teraz postać :
#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;
handle result( abi::__cxa_demangle(name, NULL, NULL, &status) );
return (status==0) ? result.p : name ;
}
#else
std::string demangle(const char* name) {
return name;
}
#endif
(Aktualizacja z 8 września 2013 r.)
Zaakceptowana odpowiedź (stan na 7 września 2013 r.) , Gdy wywołanie się abi::__cxa_demangle()
powiedzie, zwraca wskaźnik do lokalnej tablicy przydzielonej stosowi ... ach!
Zauważ również, że jeśli podasz bufor, abi::__cxa_demangle()
zakłada się, że jest on przydzielony na stercie. Przydzielanie bufora na stosie jest błędem (z dokumentacji gnu): "Jeśli output_buffer
nie jest wystarczająco długi, jest rozszerzany za pomocą realloc
." Wywołanie realloc()
wskaźnika do stosu ... och! (Zobacz też miły komentarz Igora Skochinsky'ego ).
Możesz łatwo zweryfikować oba te błędy: po prostu zmniejsz rozmiar bufora w akceptowanej odpowiedzi (od 7 września 2013) z 1024 do mniejszego, na przykład 16 i nadaj mu coś o nazwie nie dłuższej niż 15 (tak realloc()
jest nie wezwany). Mimo to, w zależności od systemu i optymalizacji kompilatora, wynikiem będzie: śmieci / nic / awaria programu.
Aby zweryfikować drugi błąd: ustaw rozmiar bufora na 1 i wywołaj go czymś, którego nazwa jest dłuższa niż 1 znak. Po uruchomieniu program prawie na pewno ulega awarii, gdy próbuje wywołać realloc()
wskaźnik do stosu.
(Stara odpowiedź z 27 grudnia 2010)
Ważne zmiany wprowadzone w kodzie KeithB : bufor musi być przydzielony przez malloc lub określony jako NULL. NIE umieszczaj go na stosie.
Dobrze jest również sprawdzić ten status.
Nie udało mi się znaleźć HAVE_CXA_DEMANGLE
. Sprawdzam __GNUG__
chociaż to nie gwarantuje, że kod się skompiluje. Czy ktoś ma lepszy pomysł?
#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>
. W przeciwnym razie działało świetnie, dzięki.output_buffer
Obszar pamięci, przydzielony za pomocą malloc, o długości * bajtów, w którym jest przechowywana zdemanalizowana nazwa. Jeśli bufor_wyjściowy nie jest wystarczająco długi, jest rozszerzany za pomocą funkcji realloc. output_buffer może zamiast tego mieć wartość NULL; w takim przypadku odkodowana nazwa jest umieszczana w obszarze pamięci przydzielonym za pomocą malloc.abi::__cxa_demangle
spodziewałem się, że zostanie on przydzielony na stercie. ” Dziękuję bardzo za sprawdzenie tego dokumentu!ret_val
rzucone podczas budowy. Możesz użyć osłony lunety, aby się przed tym ustrzec.std::unique_ptr<char, decltype(&std::free)>
jako sygnatury wskaźnika.Rdzeń wspomagający zawiera demanglera. 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> }
Jest to w zasadzie tylko opakowanie
abi::__cxa_demangle
, jak sugerowano wcześniej.źródło
To jest to, czego używamy. HAVE_CXA_DEMANGLE jest ustawiane tylko wtedy, gdy jest dostępne (tylko najnowsze wersje 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
źródło
#include <cxxabi.h>
.Tutaj spójrz na type_strings.hpp, który zawiera funkcję, która robi to, co chcesz.
Jeśli szukasz tylko narzędzia do demanglingu, którego mógłbyś np. Użyć do zmiany rzeczy wyświetlanych w pliku dziennika, spójrz na
c++filt
, które jest dostarczane z binutils. Może rozróżniać nazwy symboli C ++ i Java.źródło
abi::__cxa_demangle()
i jej podobne z<cxxabi.h>
nie są specyficzne dla GCC - mogły być tylko GCC w odległej przeszłości, ale w czasie pisania tego postu<cxxabi.h>
były głęboko zakorzenionym standardem ad hoc. Więc chociaż link do kodu odpowiedzi brzmiał DOI, mogę ręczyć, że Clang zapewni wsparcie najwyższej klasy w tym przypadku… qv, zelibcxxabi
źródła Clanga : odpowiedni decl, impl, huge test: git.io/vRTBo , git.io/vRTBh , git.io/vRTRf - w komentarzach do kodu testu zauważono , że implementacja Clang może być w jakiś sposób bardziej wymagająca niż GCC.Jest zdefiniowana implementacja, więc nie jest to coś, co będzie przenośne. W MSVC ++ name () jest nazwą niezdekorowaną i musisz spojrzeć na raw_name (), aby uzyskać ozdobioną.
Tylko dźgnięcie w ciemność tutaj, ale pod gcc, możesz spojrzeć na demangle.h
źródło
Znalazłem też makro o nazwie
__PRETTY_FUNCTION__
, które załatwia sprawę. Daje ładną nazwę funkcji (figury :)). To jest to, czego potrzebowałem.To znaczy daje mi następujące informacje:
virtual bool mutex::do_unlock()
Ale nie sądzę, żeby to działało na innych kompilatorach.
źródło
Nie jest to kompletne rozwiązanie, ale warto przyjrzeć się definicji niektórych standardowych (lub szeroko obsługiwanych) makr. Często zdarza się, że w kodzie logowania widać użycie makr:
__FUNCTION__ __FILE__ __LINE__ e.g.: log(__FILE__, __LINE__, __FUNCTION__, mymessage);
źródło
Niewielka odmiana rozwiązania Ali. Jeśli chcesz, aby kod nadal był bardzo podobny do
typeid(bla).name()
,pisząc to zamiast tego
Typeid(bla).name()
(różniące się tylko dużą pierwszą literą)to możesz być tym zainteresowany:
W pliku 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 pozostaje taki sam jak w rozwiązaniu Ali
źródło
Zobacz,
__cxa_demangle
które możesz znaleźć pod adresemcxxabi.h
.źródło
// 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; }
źródło
Przyjętego rozwiązania [1] działa w większości dobrze. Znalazłem przynajmniej jeden przypadek (i nie nazwałbym tego przypadkiem narożnym), w którym nie zgłasza tego, czego się spodziewałem ... z referencjami.
W takich przypadkach znalazłem inne rozwiązanie, zamieszczone na dole.
Przypadek problematyczny (używanie
type
zgodnie z definicją w [1]):int i = 1; cout << "Type of " << "i" << " is " << type(i) << endl; int & ri = i; cout << "Type of " << "ri" << " is " << type(ri) << endl;
produkuje
Type of i is int Type of ri is int
Rozwiązanie (używając
type_name<decltype(obj)>()
, zobacz kod poniżej):cout << "Type of " << "i" << " is " << type_name<decltype(i)>() << endl; cout << "Type of " << "ri" << " is " << type_name<decltype(ri)>() << endl;
produkuje
Type of i is int Type of ri is int&
zgodnie z życzeniem (przynajmniej przeze mnie)
Kod . Musi znajdować się w dołączonym nagłówku, a nie w osobno skompilowanym źródle, ze względu na problemy ze specjalizacją. Zobacz na przykład niezdefiniowane odniesienie do funkcji szablonu .
#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; }
źródło
Zawsze chciałem użyć type_info, ale jestem pewien, że wynik funkcji składowej name () jest niestandardowy i niekoniecznie zwróci cokolwiek, co można przekonwertować na znaczący wynik.
Jeśli trzymasz się jednego kompilatora, być może istnieje funkcja specyficzna dla kompilatora, która zrobi to, co chcesz. Sprawdź dokumentację.
źródło
Idąc za rozwiązaniem Ali, oto alternatywa oparta na szablonie C ++ 11, która działała najlepiej w moim przypadku.
// 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(); }
Stosowanie:
// 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; }
Wydrukuje:
double int test::SomeStruct
źródło