Entzerren des Ergebnisses von std::type_info::name

Lesezeit: 11 Minuten

Entzerren des Ergebnisses von stdtype infoname
Endstation

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

Gibt es eine Möglichkeit, daraus etwas Brauchbares zu machen? Wie std::vector<int> für das obige Beispiel. Wenn es nur für Nicht-Template-Klassen 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 ist für die Protokollierung, also ist es nicht so wichtig, dass es nicht ausgeschaltet werden kann, aber es sollte beim Debuggen hilfreich sein.

Entzerren des Ergebnisses von stdtype infoname
Ali

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-Features und eine andere nur mit C++98-Features.

Im Ordner Typ.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
}

#endif

Im Ordner Typ.cpp (benötigt 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

Verwendungszweck:

#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*

Art des Pointees: 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 C++11-Features nicht verwenden können, können Sie dies in C++98, der Datei Typ.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 08.09.2013)

Die akzeptierte Antwort (Stand: 7. September 2013), wenn der Anruf an abi::__cxa_demangle() ist erfolgreich, gibt einen Zeiger auf ein lokales, vom Stapel zugewiesenes Array zurück… autsch!
Beachten Sie auch, dass, wenn Sie einen Puffer bereitstellen, abi::__cxa_demangle() geht davon aus, dass es auf dem Heap allokiert ist. Das Zuweisen des Puffers auf dem Stapel ist ein Fehler (aus dem GNU-Dokument): “Wenn output_buffer ist nicht lang genug, wird es mit erweitert realloc.” Berufung realloc() auf einem Zeiger auf den Stack… autsch! (Siehe auch den freundlichen Kommentar von Igor Skochinsky.)

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 ihm etwas mit einem Namen nicht länger als 15 (bzw realloc() ist nicht namens). Abhängig von Ihrem System und den Compiler-Optimierungen lautet die Ausgabe jedoch: Müll / nichts / Programmabsturz.
Um den zweiten Fehler zu überprüfen: 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 mit ziemlicher Sicherheit beim Aufrufversuch ab realloc() mit einem Zeiger auf den Stack.


(Die alte Antwort vom 27.12.2010)

Wichtige Änderungen am Code von KeithB: der Puffer muss entweder von malloc allokiert oder als NULL angegeben werden. Ordnen Sie es NICHT dem Stack 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;
}

  • Können Sie erklären, warum der Puffer nicht auf dem Stapel zugewiesen werden kann? Denn bisher hat es bei mir gut geklappt.

    – Endstation

    23. Mai 2012 um 18:56 Uhr

  • Von dem Dokumente: output_buffer Ein mit malloc zugewiesener Speicherbereich mit *Länge Bytes, in dem der entschlüsselte Name gespeichert wird. Wenn output_buffer nicht lang genug ist, wird er mit realloc erweitert. output_buffer kann stattdessen NULL sein; In diesem Fall wird der entschlüsselte Name in einem mit malloc zugewiesenen Speicherbereich platziert.

    – Igor Skochinsky

    29. August 2012 um 18:31 Uhr

  • @IgorSkochinsky Ja, in meinem vorherigen Kommentar ist ein Tippfehler, aber ich kann das nicht bearbeiten. Was ich schreiben wollte: “Ich habe das letzte Mal nachgesehen abi::__cxa_demangle erwartet, dass es zugeteilt wird auf dem Haufen.„Vielen Dank für das Nachschlagen des Dokuments!

    – Ali

    29. August 2012 um 19:05 Uhr

  • Beachten Sie, dass dies technisch gesehen undicht werden kann, wenn ret_val wirft während des Baus. Dagegen können Sie mit einem Scope Guard vorbeugen.

    – GManNickG

    11. August 2013 um 19:18 Uhr


  • Wenn wäre wahrscheinlich klarer zu verwenden std::unique_ptr<char, decltype(&std::free)> als Signatur für Ihren Zeiger.

    – Mindvirus

    25. August 2014 um 17:53 Uhr

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_demanglewie bereits angedeutet.

  • Wenn Boost eine Option ist, ist dies der beste Weg!

    – hbobenicio

    22. November 2016 um 21:07 Uhr

Das verwenden wir. HAVE_CXA_DEMANGLE wird nur gesetzt, wenn verfügbar (nur neuere 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  

  • Sie müssen einschließen #include <cxxabi.h>.

    – fuenfundachtzig

    20. Januar 2010 um 18:37 Uhr

  • Interessant. Ich habe __cxa_demangle ohne HAVE_CXA_DEMANGLE definiert

    – Matt K

    10. November 2010 um 20:52 Uhr

  • @Matt Was ich sagen wollte ist, dass unser Build-System, basierend auf autoconf, nur HAVE_CXA_DEMANGLE setzt, wenn es verfügbar ist.

    – KeithB

    10. November 2010 um 22:11 Uhr

  • WARNUNG! Der obige Code führt wahrscheinlich zum Absturz des Programms. Der Puffer muss entweder von malloc allokiert oder als NULL angegeben werden. Ordnen Sie es NICHT dem Stack zu. Siehe meinen Code unten.

    – Ali

    27. Dezember 2010 um 20:14 Uhr

  • Achtung, res könnte NULL zurückgeben 🙂

    – Zibri

    4. Mai 2013 um 14:18 Uhr

Entzerren des Ergebnisses von stdtype infoname
Johannes Schaub – litb

Hier, schau mal type_strings.hpp Es enthält eine Funktion, die das tut, was Sie wollen.

Wenn Sie nur nach einem Entwirrungswerkzeug suchen, mit dem Sie zB in einer Protokolldatei angezeigte Dinge entwirren können, schauen Sie sich das an c++filt, das mit binutils geliefert wird. Es kann C++- und Java-Symbolnamen demanglen.

1646913011 419 Entzerren des Ergebnisses von stdtype infoname
Human-Compiler

Wenn wir zum Zwecke der Protokollierung nur den unverfälschten Typnamen wollen, können wir dies tatsächlich ohne Verwendung tun std::type_info oder überhaupt RTTI.

Eine leicht portable Lösung, die für die drei großen Haupt-Compiler-Frontends (gcc, clang und msvc) funktioniert, wäre die Verwendung einer Funktion template und extrahieren Sie den Typnamen aus dem Funktionsnamen.

gcc und clang beide bieten __PRETTY_FUNCTION__ Dies ist der Name einer aktuellen Funktion oder Funktionsvorlage mit allen Typargumenten in der Zeichenfolge. Ähnlich hat MSVC __FUNCSIG__ was äquivalent ist. Diese sind jeweils etwas anders formatiert, zum Beispiel für einen Anruf von void foo<int>geben die Compiler etwas anderes aus:

  • gcc formatiert ist void foo() [with T = int; ]
  • clang formatiert ist void foo() [T = int]
  • msvc formatiert ist void foo<int>()

Wenn Sie dies wissen, müssen Sie nur ein Präfix und ein Suffix analysieren und in eine Funktion packen, um den Typnamen zu extrahieren.

Wir können sogar c++17 verwenden std::string_view und erweitert constexpr um Zeichenfolgennamen zu erhalten Kompilierzeit, indem Sie einfach den Namen einer Vorlagenfunktion analysieren. Dies könnte auch in jeder früheren C++-Version durchgeführt werden, aber dies erfordert immer noch eine Art String-Parsing.

Zum Beispiel:

#include <string_view>

template <typename T>
constexpr auto get_type_name() -> std::string_view
{
#if defined(__clang__)
    constexpr auto prefix = std::string_view{"[T = "};
    constexpr auto suffix = "]";
    constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
#elif defined(__GNUC__)
    constexpr auto prefix = std::string_view{"with T = "};
    constexpr auto suffix = "; ";
    constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
#elif defined(__MSC_VER)
    constexpr auto prefix = std::string_view{"get_type_name<"};
    constexpr auto suffix = ">(void)";
    constexpr auto function = std::string_view{__FUNCSIG__};
#else
# error Unsupported compiler
#endif

    const auto start = function.find(prefix) + prefix.size();
    const auto end = function.find(suffix);
    const auto size = end - start;

    return function.substr(start, size);
}

Damit können Sie telefonieren get_type_name<T>() ein bekommen std::string_view zur Kompilierzeit, die den unverfälschten Typnamen angibt.

Zum Beispiel:

std::cout << get_type_name<std::string>() << std::endl;

auf GCC wird ausgegeben:

std::__cxx11::basic_string<char>

und on clang wird ausgegeben:

std::basic_string<char>

Live-Beispiel


Eine ähnliche Erweiterung zu diesem Ansatz, der a vermeidet prefix und suffix ist, davon auszugehen, dass der Funktionsname für alle Typen gleich ist, und nach einem Sentinel-Typ zu suchen, um den Offset zum Sentinel von jedem Ende aus zu analysieren. Dadurch wird sichergestellt, dass die Zeichenfolgensuche nur einmal erfolgt und angenommen wird, dass der Offset jedes Mal den Zeichenfolgennamen findet. Zum Beispiel mit double als einfacher Wächter:

template <typename T>
constexpr auto full_function_name() -> std::string_view
{
#if defined(__clang__) || defined(__GNUC__)
    return std::string_view{__PRETTY_FUNCTION__};
#elif defined(__MSC_VER)
    return std::string_view{__FUNCSIG__};
#else
# error Unsupported compiler
#endif
}

// Outside of the template so its computed once
struct type_name_info {
    static constexpr auto sentinel_function = full_function_name<double>();
    static constexpr auto prefix_offset = sentinel_function.find("double");
    static constexpr auto suffix_offset = sentinel_function.size() - sentinel_function.rfind("double");
};

template <typename T>
constexpr auto get_type_name() -> std::string_view
{
    constexpr auto function = full_function_name<T>();

    const auto start = type_name_info::prefix_offset;
    const auto end = function.size() - type_name_info::suffix_offset;
    const auto size = end - start;

    return function.substr(start, size);
}

Live-Beispiel


Dies ist nicht übertragbar alle Compiler, kann aber für jeden Compiler modifiziert werden, der a anbietet __FUNCSIG__/__PRETTY_FUNCTION__ Äquivalent; es erfordert nur ein wenig Parsing.

Hinweis: Das war nicht völlig getestet, daher kann es einige Fehler geben; aber die Hauptidee ist, jede Ausgabe zu parsen, die den Namen vollständig enthält – was oft ein Nebeneffekt davon ist __func__-ähnliche Ausgaben auf Compilern.

  • Traurig, dass selbst im Jahr 2021 zu viel Boiler-Plate-Code erforderlich ist, um C ++ – Entwirrung zu erhalten 🙁

    – usr1234567

    14. März 2021 um 9:44 Uhr

  • Einverstanden! Hoffentlich wird C++23 endlich Unterstützung für statische Reflektion beinhalten, sodass sich die Leute nicht auf diese Art von ruckelndem Ansatz verlassen müssen

    – Human-Compiler

    14. März 2021 um 16:32 Uhr

Es ist implementierungsdefiniert, also ist es nicht etwas, das portabel sein wird. In MSVC++ ist name() der nicht verzierte Name, und Sie müssen sich raw_name() ansehen, um den verzierten Namen zu erhalten.
Hier nur ein Stich ins Blaue, aber unter gcc sollten Sie sich das ansehen demangle.h

  • Traurig, dass selbst im Jahr 2021 zu viel Boiler-Plate-Code erforderlich ist, um C ++ – Entwirrung zu erhalten 🙁

    – usr1234567

    14. März 2021 um 9:44 Uhr

  • Einverstanden! Hoffentlich wird C++23 endlich Unterstützung für statische Reflektion beinhalten, sodass sich die Leute nicht auf diese Art von ruckelndem Ansatz verlassen müssen

    – Human-Compiler

    14. März 2021 um 16:32 Uhr

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

Dh es gibt mir folgendes:

virtual bool mutex::do_unlock()

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

  • Jawohl, SCHÖNE_FUNKTION ist gcc-spezifisch.

    – Greg Rogers

    11. November 2008 um 20:33 Uhr

987800cookie-checkEntzerren des Ergebnisses von std::type_info::name

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy