325 Stimmen

Wie kann ich Reflection zu einer C++-Anwendung hinzufügen?

Ich möchte in der Lage sein, eine C++-Klasse auf ihren Namen, ihren Inhalt (d.h. Mitglieder und ihre Typen) usw. zu untersuchen. Ich spreche hier von nativem C++, nicht von verwaltetem C++, das Reflection hat. Ich weiß, dass C++ mit RTTI einige begrenzte Informationen liefert. Welche zusätzlichen Bibliotheken (oder andere Techniken) könnten diese Informationen liefern?

21 Stimmen

Pech gehabt, ohne Makros und andere Vorverarbeitungen geht es nicht, denn die erforderlichen Metadaten gibt es nicht es sei denn, Sie erstellen sie manuell mit Hilfe von Makro-Vorverarbeitungsmagie.

7 Stimmen

Die Informationen, die Sie von RTTI erhalten können, reichen jedoch nicht aus, um die meisten Dinge zu tun, für die Sie eigentlich eine Reflexion benötigen würden. Sie können zum Beispiel nicht über die Mitgliedsfunktionen einer Klasse iterieren.

2voto

Naore Azenkut Punkte 31

Auch wenn Reflection in C++ nicht sofort unterstützt wird, ist es nicht allzu schwer zu implementieren. Ich bin auf diesen großartigen Artikel gestoßen: http://replicaisland.blogspot.co.il/2010/11/building-reflective-object-system-in-c.html

Der Artikel erklärt sehr detailliert, wie man ein ziemlich einfaches und rudimentäres Reflexionssystem implementieren kann. Zugegeben, es ist nicht die gesündeste Lösung, und es gibt noch einige Ecken und Kanten, die aussortiert werden müssen, aber für meine Bedürfnisse war es ausreichend.

Fazit: Reflexion kann sich auszahlen, wenn man sie richtig einsetzt, und sie ist in C++ durchaus machbar.

2voto

Siehe Classdesc http://classdesc.sf.net . Es bietet Reflexion in Form von Klassen-"Deskriptoren", funktioniert mit jedem Standard-C++-Compiler (ja, es ist bekannt, dass es sowohl mit Visual Studio als auch mit GCC funktioniert) und erfordert keine Quellcode-Annotation (obwohl es einige Pragmas gibt, um schwierige Situationen zu bewältigen). Es wird seit mehr als einem Jahrzehnt entwickelt und in einer Reihe von Projekten im industriellen Maßstab eingesetzt.

1 Stimmen

Willkommen bei Stack Overflow. Obwohl diese Antwort zum Thema gehört, ist es wichtig, darauf hinzuweisen, dass Sie der Autor dieser Software sind, um deutlich zu machen, dass es sich nicht um eine unvoreingenommene Empfehlung handelt :-)

2voto

Edward Strange Punkte 39597

Als ich Reflexion in C++ wollte, las ich dieser Artikel und verbesserte das, was ich dort sah. Tut mir leid, das geht nicht. Ich besitze das Ergebnis nicht... aber Sie können sicherlich bekommen, was ich hatte und von dort aus weitergehen.

Wenn ich Lust habe, untersuche ich derzeit Methoden zur Verwendung von inherit_linearly, um die Definition von reflektierbaren Typen zu vereinfachen. Ich bin eigentlich schon ziemlich weit gekommen, aber ich habe noch einen weiten Weg vor mir. Die Änderungen in C++0x werden mir in diesem Bereich sehr wahrscheinlich eine große Hilfe sein.

1voto

Eugene G Punkte 11

Ich möchte auf die Existenz des automatischen Introspektions-/Reflexions-Toolkits "IDK" hinweisen. Es verwendet einen Meta-Compiler wie der von Qt und fügt Metainformationen direkt in Objektdateien ein. Es wird behauptet, dass es einfach zu benutzen ist. Keine externen Abhängigkeiten. Es erlaubt Ihnen sogar, std::string automatisch zu reflektieren und dann in Skripten zu verwenden. Bitte schauen Sie sich IDK

1voto

jenkas Punkte 687

Reflection in C++ ist sehr nützlich, in Fällen, in denen Sie benötigen, um einige Methode für jedes Mitglied (zum Beispiel: Serialisierung, Hashing, vergleichen). Ich kam mit generischen Lösung, mit sehr einfachen Syntax:

struct S1
{
    ENUMERATE_MEMBERS(str,i);
    std::string str;
    int i;
};
struct S2
{
    ENUMERATE_MEMBERS(s1,i2);
    S1 s1;
    int i2;
};

Dabei ist ENUMERATE_MEMBERS ein Makro, das später beschrieben wird (UPDATE):

Angenommen, wir haben eine Serialisierungsfunktion für int und std::string wie folgt definiert:

void EnumerateWith(BinaryWriter & writer, int val)
{
    //store integer
    writer.WriteBuffer(&val, sizeof(int));
}
void EnumerateWith(BinaryWriter & writer, std::string val)
{
    //store string
    writer.WriteBuffer(val.c_str(), val.size());
}

Und wir haben eine generische Funktion in der Nähe des "geheimen Makros" ;)

template<typename TWriter, typename T>
auto EnumerateWith(TWriter && writer, T && val) -> is_enumerable_t<T>
{
    val.EnumerateWith(write); //method generated by ENUMERATE_MEMBERS macro
}

Jetzt können Sie schreiben

S1 s1;
S2 s2;
//....
BinaryWriter writer("serialized.bin");

EnumerateWith(writer, s1); //this will call EnumerateWith for all members of S1
EnumerateWith(writer, s2); //this will call EnumerateWith for all members of S2 and S2::s1 (recursively)

Mit dem ENUMERATE_MEMBERS-Makro in der struct-Definition können Sie Serialisierung, Vergleich, Hashing und andere Funktionen erstellen, ohne den Originaltyp zu berühren. Die einzige Anforderung ist die Implementierung der "EnumerateWith"-Methode für jeden Typ, der nicht aufzählbar ist, per Enumerator (wie BinaryWriter). Normalerweise müssen Sie 10-20 "einfache" Typen implementieren, um jeden Typ in Ihrem Projekt zu unterstützen.

Dieses Makro sollte Null-Overhead für die Erstellung/Zerstörung von Strukturen zur Laufzeit haben, und der Code von T.EnumerateWith() sollte bei Bedarf generiert werden, was erreicht werden kann, indem man es zu einer Template-Inline-Funktion macht, so dass der einzige Overhead in der ganzen Geschichte darin besteht, ENUMERATE_MEMBERS(m1,m2,m3...) zu jeder Struktur hinzuzufügen, während die Implementierung einer spezifischen Methode pro Mitgliedstyp ein Muss in jeder Lösung ist, so dass ich es nicht als Overhead annehme.

UPDATE: Es ist sehr einfache Implementierung von ENUMERATE_MEMBERS Makro (jedoch könnte es ein wenig erweitert werden, um Vererbung von enumerable struct zu unterstützen)

#define ENUMERATE_MEMBERS(...) \
template<typename TEnumerator> inline void EnumerateWith(TEnumerator & enumerator) const { EnumerateWithHelper(enumerator, __VA_ARGS__ ); }\
template<typename TEnumerator> inline void EnumerateWith(TEnumerator & enumerator) { EnumerateWithHelper(enumerator, __VA_ARGS__); }

// EnumerateWithHelper
template<typename TEnumerator, typename ...T> inline void EnumerateWithHelper(TEnumerator & enumerator, T &...v) 
{ 
    int x[] = { (EnumerateWith(enumerator, v), 1)... }; 
}

// Generic EnumerateWith
template<typename TEnumerator, typename T>
auto EnumerateWith(TEnumerator & enumerator, T & val) -> std::void_t<decltype(val.EnumerateWith(enumerator))>
{
    val.EnumerateWith(enumerator);
}

Und für diese 15 Zeilen Code benötigen Sie keine Bibliothek eines Drittanbieters ;)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X