Entwirrung des Ergebnisses von std :: type_info :: name

93

Ich arbeite derzeit an einem Protokollierungscode, der unter anderem Informationen über die aufrufende Funktion drucken soll. Dies sollte relativ einfach sein, Standard C ++ hat eine type_infoKlasse. Dies enthält den Namen der Typ-ID-Klasse / Funktion / etc. aber es ist verstümmelt. Es ist nicht sehr nützlich. Dh typeid(std::vector<int>).name()zurück St6vectorIiSaIiEE.

Gibt es eine Möglichkeit, daraus etwas Nützliches zu machen? Wie std::vector<int>für das obige Beispiel. Wenn es nur für Klassen ohne Vorlage funktioniert, ist das auch in Ordnung.

Die Lösung sollte für gcc funktionieren, aber es wäre besser, wenn ich sie portieren könnte. Es dient zum Protokollieren, daher ist es nicht so wichtig, dass es nicht deaktiviert werden kann, aber es sollte beim Debuggen hilfreich sein.

Terminus
quelle

Antworten:

117

Angesichts der Aufmerksamkeit, die diese Frage / Antwort erhält, und des wertvollen Feedbacks von GManNickG habe ich den Code ein wenig aufgeräumt. Es werden zwei Versionen angegeben: eine mit C ++ 11-Funktionen und eine mit nur C ++ 98-Funktionen.

In der Datei 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

In der Datei type.cpp (erfordert 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

Verwendung:

#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;
}

Es druckt:

Typ von ptr_base: Base*
Typ von Pointee:Derived

Getestet mit g ++ 4.7.2, g ++ 4.9.0 20140302 (experimentell), clang ++ 3.4 (Trunk 184647), clang 3.5 (Trunk 202594) unter Linux 64 Bit und g ++ 4.7.2 (Mingw32, Win32 XP SP2).

Wenn Sie nicht C ++ 11 - Funktionen verwenden können, hier ist , wie es in C ++ 98 durchgeführt werden kann, wird die Datei type.cpp ist jetzt:

#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


(Update vom 8. September 2013)

Die akzeptierte Antwort (Stand: 7. September 2013) gibt bei abi::__cxa_demangle()erfolgreichem Aufruf von einen Zeiger auf ein lokales Array mit Stapelzuweisung zurück ... autsch!
Beachten Sie außerdem, dass bei der Bereitstellung eines Puffers abi::__cxa_demangle()davon ausgegangen wird, dass dieser auf dem Heap zugewiesen ist. Das Zuweisen des Puffers auf dem Stapel ist ein Fehler (aus dem Gnu-Dokument): "Wenn output_bufferes nicht lang genug ist, wird es mit erweitert realloc." Der Aufruf realloc()auf einen Zeiger auf den Stapel ... autsch! (Siehe auch Igor Skochinskys freundlichen Kommentar.)

Sie können diese beiden Fehler leicht überprüfen: Reduzieren Sie einfach die Puffergröße in der akzeptierten Antwort (Stand: 7. September 2013) von 1024 auf etwas Kleineres, z. B. 16, und geben Sie etwas mit einem Namen an, der nicht länger als 15 ist (so realloc()ist es auch) nicht genannt). Abhängig von Ihrem System und den Compiler-Optimierungen lautet die Ausgabe jedoch: Garbage / Nothing / Program Crash.
So überprüfen Sie den zweiten Fehler: Setzen Sie die Puffergröße auf 1 und rufen Sie sie mit etwas auf, dessen Name länger als 1 Zeichen ist. Wenn Sie es ausführen, stürzt das Programm fast sicher ab, wenn es versucht, realloc()mit einem Zeiger auf den Stapel aufzurufen .


(Die alte Antwort vom 27. Dezember 2010)

Wichtige Änderungen am KeithB-Code : Der Puffer muss entweder von malloc zugewiesen oder als NULL angegeben werden. Ordnen Sie es NICHT dem Stapel zu.

Es ist ratsam, auch diesen Status zu überprüfen.

Ich konnte nicht finden HAVE_CXA_DEMANGLE. Ich überprüfe, __GNUG__obwohl dies nicht garantiert, dass der Code überhaupt kompiliert wird. Hat jemand eine bessere Idee?

#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;
}
Ali
quelle
Beachten Sie, dass dies erforderlich ist #include <cxxabi.h>. Ansonsten hat super geklappt, danke.
Jterrace
2
Aus den Dokumenten : output_bufferEin mit malloc zugewiesener Speicherbereich mit * Längenbytes, in dem der entwirrte Name gespeichert ist. Wenn output_buffer nicht lang genug ist, wird es mit realloc erweitert. output_buffer kann stattdessen NULL sein; In diesem Fall wird der entwirrte Name in einem Speicherbereich abgelegt, der malloc zugewiesen ist.
Igor Skochinsky
2
@IgorSkochinsky Ja, mein vorheriger Kommentar enthält einen Tippfehler, den ich jedoch nicht bearbeiten kann. Was ich schreiben wollte: "Als ich das letzte Mal nachgesehen habe, habe ich abi::__cxa_demangleerwartet, dass es auf dem Heap zugewiesen wird . " Vielen Dank, dass Sie das Dokument nachgeschlagen haben!
Ali
1
Beachten Sie, dass dies technisch auslaufen kann, wenn ret_valwährend des Baus geworfen wird. Sie können einen Zielfernrohrschutz verwenden, um sich dagegen zu schützen.
GManNickG
3
Wenn wäre wahrscheinlich klarer std::unique_ptr<char, decltype(&std::free)>als Signatur für Ihren Zeiger zu verwenden.
Mindvirus
27

Boost Core enthält einen Demangler. Kasse 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>
}

Es ist im Grunde nur ein Wrapper für abi::__cxa_demangle, wie bereits vorgeschlagen wurde.

moof2k
quelle
1
Wenn Boost eine Option ist, ist dies der beste Weg!
Hbobenicio
13

Das verwenden wir. HAVE_CXA_DEMANGLE wird nur festgelegt, wenn verfügbar (nur aktuelle Versionen von 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  
KeithB
quelle
6
Sie müssen einschließen #include <cxxabi.h>.
Fuenfundachtzig
Interessant. Ich habe __cxa_demangle ohne HAVE_CXA_DEMANGLE definiert
mkb
@Matt Was ich sagen wollte ist, dass unser Build-System, das auf Autoconf basiert, HAVE_CXA_DEMANGLE nur setzt, wenn es verfügbar ist.
KeithB
19
WARNUNG! Der obige Code führt wahrscheinlich zum Absturz des Programms. Der Puffer muss entweder von malloc zugewiesen oder als NULL angegeben werden. Ordnen Sie es NICHT dem Stapel zu. Siehe meinen Code unten.
Ali
Achtung
8

Hier finden Sie aktuelle type_strings.hpp es eine Funktion enthält , das tut , was Sie wollen.

Wenn Sie nur nach einem Entwirrungswerkzeug suchen, mit dem Sie beispielsweise in einer Protokolldatei angezeigte Inhalte entstellen können, schauen Sie sich c++filtdas mit binutils gelieferte an. Es kann C ++ - und Java-Symbolnamen entwirren.

Johannes Schaub - litb
quelle
Zu beachten ist, dass sowohl cxa_demange () (mit dem der verknüpfte Code verwendet wird) als auch cx ++ filt gcc-spezifisch sind. Es gibt keine tragbare Möglichkeit, dies zu tun.
KeithB
c ++ filt schneidet es nicht, ich brauche dieses Zeug (oder das meiste davon) zur Kompilierungszeit, meistens mit Makros.
Endstation
4
Der Link zu type_strings.cpp scheint unterbrochen zu sein.
StackedCrooked
1
Hi @GregoryPakosz Der Github-Link in Ihrem obigen Kommentar scheint ebenfalls defekt zu sein :( Cheers
olibre
Nur ein wichtiges FYI: abi::__cxa_demangle()und seine Herkunft <cxxabi.h> ist nicht GCC-spezifisch - sie waren in der fernen Vergangenheit möglicherweise nur GCC-spezifisch , aber zum Zeitpunkt des Schreibens dieses Beitrags <cxxabi.h>war dies ein fest verwurzelter Ad-hoc-Standard. Während der Code-Link der Antwort DOI war, kann ich dafür bürgen, dass Clang in diesem Fall erstklassigen Support bietet… siehe auch aus der libcxxabiQuelle des Clang : der jeweilige deklarierte, implizite, riesige Test: git.io/vRTBo , git.io/vRTBh , git.io/vRTRf - In den Kommentaren des Testcodes wird darauf hingewiesen, dass die Clang-Implementierung im Vergleich zu GCC in der Lage ist, mehr zu entwirren.
fish2000
4

Die Implementierung ist definiert, daher ist sie nicht portabel. In MSVC ++ ist name () der nicht dekorierte Name, und Sie müssen sich raw_name () ansehen, um den dekorierten zu erhalten.
Nur ein Stich in die Dunkelheit hier, aber unter gcc möchten Sie vielleicht demangle.h anschauen

Finsternis
quelle
3

Keine vollständige Lösung, aber Sie sollten sich ansehen, was einige der Standardmakros (oder weit verbreitete Makros) definieren. Bei der Protokollierung von Code wird häufig die Verwendung der Makros angezeigt:

__FUNCTION__
__FILE__
__LINE__

e.g.:

log(__FILE__, __LINE__, __FUNCTION__, mymessage);
Luke
quelle
4
Ganz zu schweigen von PRETTY_FUNCTION .
CesarB
1
Dadurch erhalten Sie Informationen darüber, wo Sie sich im Code befinden. Was die Frage stellte, war ein hübscher Name eines Typs wie std :: vector.
KeithB
Er erwähnte, es sei zum Debuggen gedacht, und ich erklärte, es sei keine vollständige Lösung. Andere Makros wie FUNCDNAME geben den dekorierten Namen zurück.
Luke
Beim erneuten Lesen der Frage hieß es: "Ich arbeite derzeit an einem Protokollierungscode, der unter anderem Informationen über die aufrufende Funktion drucken soll." Das funktioniert.
Max Lybbert
Es ist nicht vollständig, da ich den Namespace nicht kenne. Dies ist bereits in meinem Code enthalten. Danke trotzdem.
Endstation
3

Ich habe auch ein Makro namens gefunden __PRETTY_FUNCTION__, das den Trick macht. Es gibt einen hübschen Funktionsnamen (Zahlen :)). Das brauchte ich.

Dh es gibt mir folgendes:

virtual bool mutex::do_unlock()

Aber ich denke nicht, dass es auf anderen Compilern funktioniert.

Terminus
quelle
Ja, PRETTY_FUNCTION ist gcc-spezifisch.
Greg Rogers
2

Eine leichte Variation von Alis Lösung. Wenn Sie möchten, dass der Code immer noch sehr ähnlich ist

typeid(bla).name(),

schreibe dies stattdessen

Typeid(bla).name() (unterscheidet sich nur im Großbuchstaben)

dann könnte Sie das interessieren:

In der Datei 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 bleibt wie in Alis Lösung

matzzz
quelle
1

Schauen Sie sich an, __cxa_demanglewas Sie unter finden können cxxabi.h.

CesarB
quelle
Ich habe genommen, es ist veraltet, gemäß der Nachricht, die ich bekomme.
Endstation
Wo haben Sie diese Nachricht gefunden? Ich habe es nur gegoogelt und es scheint unterstützt zu werden, keine Anzeichen dafür, dass es veraltet ist.
Ali
Vielleicht ist es im :: Namespace veraltet. Verwenden Sie abi :: __ cxa_demangle und Sie erhalten keine Warnung. Welches gcc benutzt du?
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;
  }
Dan Dare
quelle
11
WARNUNG! Der Puffer muss entweder von malloc zugewiesen oder als NULL angegeben werden. Ordnen Sie es NICHT dem Stapel zu. Siehe meinen Code unten.
Ali
1

Die akzeptierte Lösung [1] funktioniert meistens gut. Ich habe mindestens einen Fall gefunden (und ich würde ihn nicht als Eckfall bezeichnen), in dem nicht berichtet wird, was ich erwartet habe ... mit Referenzen.

Für diese Fälle habe ich eine andere Lösung gefunden, die unten angegeben ist.

Problematischer Fall (unter Verwendung typewie in [1] definiert):

int i = 1;
cout << "Type of " << "i" << " is " << type(i) << endl;
int & ri = i;
cout << "Type of " << "ri" << " is " << type(ri) << endl;

produziert

Type of i is int
Type of ri is int

Lösung (mit type_name<decltype(obj)>(), siehe Code unten):

cout << "Type of " << "i" << " is " << type_name<decltype(i)>() << endl;
cout << "Type of " << "ri" << " is " << type_name<decltype(ri)>() << endl;

produziert

Type of i is int
Type of ri is int&

wie gewünscht (zumindest von mir)

Code . Aufgrund von Spezialisierungsproblemen muss es sich in einem enthaltenen Header befinden, nicht in einer separat kompilierten Quelle. Siehe zum Beispiel den undefinierten Verweis auf die Vorlagenfunktion .

#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;
}
sancho.s ReinstateMonicaCellio
quelle
0

Ich wollte schon immer type_info verwenden, bin mir aber sicher, dass das Ergebnis der Mitgliedsfunktion name () nicht dem Standard entspricht und nicht unbedingt etwas zurückgibt, das in ein aussagekräftiges Ergebnis konvertiert werden kann.
Wenn Sie sich an einen Compiler halten, gibt es möglicherweise eine compilerspezifische Funktion, die das tut, was Sie wollen. Überprüfen Sie die Dokumentation.

Quamrana
quelle
0

Nach Alis Lösung ist hier die Alternative mit C ++ 11- Vorlagen, die für meine Verwendung am besten funktioniert hat.

// 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();
}

Verwendung:

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

Wird drucken:

double                                                                        
int                                                                           
test::SomeStruct
Alexis Paques
quelle