Indexed Property System
-
Hallo, gibt es für C++ soetwas wie ein "Indexed Property System".
Ich möchte zur Laufzeit dann ungefähr sowas machen können:window.SetValue(L"Width", 1024);
Ich habe es im Moment so gelöst, dass ich alle Properties erst beim Aufruf des Konstruktors registrieren (also in einer std::map speichern) lasse. SetValue sucht dann die Map nach dem Key ab und setzt den Wert.
-
Und was willst du jetzt? Was ist das Problem bei der std::map?
-
Reflection gibts in C++ nicht.
Du könntest aber etwas basteln, um mittels Strings zur Laufzeit auf die einzelnen Member zugreifen zu können (z.B. über Funktionszeiger). Doch die Registrierung muss jeweils manuell erfolgen.
-
Nexus schrieb:
Reflection gibts in C++ nicht.
Zu pauschal und nicht richtig, aber
- in begrenztem Umfang zur Laufzeit (RTTI, siehe dynamic_cast and std::type_info)
- über traits zur Compiletime.
Reflexionen zum Herausfinden des Namens gibt es aber in der Tat nicht. Da könnte der originale Autor sich ein wenig in Qt's Property-System einarbeiten, wo es mit Makros und einem zusätzlichen Präprozessor (moc) gelöst ist.
-
phreck schrieb:
Zu pauschal und nicht richtig
Wenn man den Begriff so verwendet wie in Java oder C#, kommt das schon hin. Im weiten Sinne spielt dann natürlich noch mehr mit.
Und sowohl RTTI als auch Traits helfen Student83 relativ wenig, falls ich seine Aufgabenstellung richtig verstanden habe.
-
@phreck:
Wenn du pingelig sein willst...phreck schrieb:
Nexus schrieb:
Reflection gibts in C++ nicht.
Zu pauschal und nicht richtig, aber
- in begrenztem Umfang zur Laufzeit (RTTI, siehe dynamic_cast and std::type_info)
Das hat mit Reflection mal nix zu tun. Bei Reflection geht es um die Struktur des Programmes selbst, von Klassen etc. Nicht darum rauszubekommen von welchem Typ ein Objekt ist.
- über traits zur Compiletime.
Das schon eher, aber naja. Ich würde nicht behaupten dass C++ deswegen "Reflection unterstützt". Könntest du genau so gut behaupten dass C Multimethods unterstützt.
-
hustbaer schrieb:
Wenn du pingelig sein willst.
Nein, nur korrekt. FUD und Vorurteile wurzeln in genau sowas. Z.B. höre ich heute noch oft genug, wie lästig es sei, in C++ verkettete Listen selbst verfassen zu müssen, weil sich diejenigen nie über Stammtischforenwissen hinaus zu erweitern versucht haben.
phreck schrieb:
Nexus schrieb:
Reflection gibts in C++ nicht.
Zu pauschal und nicht richtig, aber
- in begrenztem Umfang zur Laufzeit (RTTI, siehe dynamic_cast and std::type_info)
Das hat mit Reflection mal nix zu tun. Bei Reflection geht es um die Struktur des Programmes selbst, von Klassen etc. Nicht darum rauszubekommen von welchem Typ ein Objekt ist.[/quote]
Wenn der Typ, einer der abstrakten Typen in der Vererbungshierarchie eines Typen sowie der symbolische Name NICHT zur Programmstruktur gehören, dann hast du Recht. Dann muss ich aber auch die Existenz von C++ anzweifeln und meine eigene ebenso.
Aber naja.
-
Dass ich das ganze nur über Tricks hinbekomme ist mir klar. QT ist ein gutes Stichwort. Genau genommen suche ich soetwas wie das Q_PROPERTY:
http://doc.qt.nokia.com/4.7-snapshot/properties.html#requirements-for-declaring-properties
Allerdings verstehe ich nicht so ganz wie dieses Q_PROPERTY nun implementiert wurde. In der qobjectdef.h Datei findet sich lediglich:#define Q_PROPERTY(text) Q_PROPERTY(text)
Die genaue Implementierung des Macros habe ich noch nicht finden können. Weis zufällig jemand wie das Macro definiert ist bzw. wo ich dieses finde?
-
Die genaue Implementierung des Macros habe ich noch nicht finden können. Weis zufällig jemand wie das Macro definiert ist bzw. wo ich dieses finde?
Ich bin mir nicht sicher (habe noch nicht viel mit Qt gemacht - gefällt mir nicht wirklich), aber vielleicht hat der moc (Meta Object Compiler) da seine Finger im Spiel. Einfach mal danach suchen.
-
Ich würde nicht behaupten dass C++ deswegen "Reflection unterstützt".
Ich *meine* mich erinnern zu können, gesagt zu haben, dass C++ es in geringem Maße tut, nicht in dem Maße wie es bspw. in .net gegeben ist. Trotzdem, Typ-Introspektion ist extrem detailliert in C++, und Code-Emission/-Generierung zur Kompilierzeit ist ohne weiteres möglich. Ein gutes Beispiel sind Expression-Templates, die man fein benutzen kann, um gut leserlichen SSE-Code zu erzeugen. Und dann gibt es noch die Möglichkeit, selbst einen Compiler zu schreiben oder auf ein Framework wie LLVM zu linken und benutzen, und schon verwischen die Grenzen.
Könntest du genau so gut behaupten dass C Multimethods unterstützt.
Als First Class Citizen sicherlich nicht.
-
Wenn dafür ein Compiler nötig ist fällt das sowieso weg. Ich möchte meine Library nicht von der QT-Library abhängig machen.
Beim googlen habe ich noch folgendes Code-Schnippsel gefunden:
<code>
class Person
{
public:
int id_;
char name_[32];
int age_;Person& get_father() const { .... }
static void describe( Descriptor & d )
{
d.add_field( "id_", &Person::id_ );
d.add_field( "name_", &Person::name_ );
d.add_method( "get_father", &Person::get_father );
}
};...
Person p;
Descriptor &d = descriptor_of( p );
for( iterator i = d.begin(); i!=d.end(); ++i )
if( i->is_method() )
cout << i->member_name() << endl;
</code>Würde das funktionieren, dass ich sozusagen jeder Klasse eine std::map bestehend aus einem Key und einer Referenz auf die zugehörige Variable mitgebe und dann später im Code die Variable über den Key finden kann.
-
Cute schrieb:
Die genaue Implementierung des Macros habe ich noch nicht finden können. Weis zufällig jemand wie das Macro definiert ist bzw. wo ich dieses finde?
Ich bin mir nicht sicher (habe noch nicht viel mit Qt gemacht - gefällt mir nicht wirklich), aber vielleicht hat der moc (Meta Object Compiler) da seine Finger im Spiel. Einfach mal danach suchen.
Hat er. Ich meinte aber, es nur mal als Inspiration anzusehen. Als Startpunkt könnte man ein Makro wie dieses nehmen:
#define PROPERTY(type, name) \ private: type name; \ public: void set##name(type const &val) { name=val; } \ type name() const { return name; } \ private:
oder einen saubereren Wrapper wie
template <typename T> class property { public: property(const char* name_, T const &value_ = T()) : name_(name_), value_(value_) {} const operator T () const { return value_; } property& operator = (T const &v) { value_ = v; return *this; } const char *name() const { return name_; } private: T value_; const char *name_; property() = delete; // Nutzer zwingen, dem Ding einen Namen zu geben };
-
(frisch registriert)
Generell denke ich aber, dass C++ nicht die richtige Sprache ist, wenn du so etwas brauchst.
-
@phreck
Den Eindruck habe ich auch, aber die Library soll nunmal plattformunabhängig und mit OpenGL verwendet werden können. Da komme ich an C++ nicht vorbei.
Mit deiner Property-Klasse kann ich allerdings keine Variablen zur Laufzeit anhand eines Key ausfindig machen. Das brauche ich unbedingt.
-
Soll ja auch nur der Startpunkt sein. Du könntest den Konstruktor derart erweitern, dass der Nutzer ihm auch noch eine Art Katalog übergeben muss, in welchen sich die property<> dann einträgt.
template <typename T> class catalog { public: catalog (const char* enclosingClassName, T &enclosingClass) = delete; ... private: catalog () = delete; }; class FooBar { catalog<FooBar> catalog_; property<int> aProperty; property<float> anotherProperty; public: FooBar () : catalog_("FooBar", *this), , aProperty("aProperty", catalog_) , anotherProperty("anotherProperty", catalog_) {} }
Eines der Features, die ich an C++ mag, ist die Möglichkeit, die Nutzer meiner Klassen zu bestimmten Aktionen zu zwingen, wie beim property<>-Beispiel.
Natürlich musst du sehr behutsam arbeiten, und dir über gewissen Feinheiten von C++ im Klaren sein, wie implizite Typkonvertierungen. Oder ersetze die Operatoren in der property<>-Klasse durch benannte get/set-Accessoren.
Übrigens könntest du mit partieller Spezialisierung oder je nach Geschmack mit bedingter Vererbung noch steuern, ob der setter private/protected (und das gleiche für den getter) sein soll.
-
Vielleicht ist dir auch der Makro-Operator
#
behilflich, wenn es darum geht, Bezeichner in Strings zu verwandeln. Wenn zusätzlich der Katalog immer gleich heisst, könnte man die Dinge so etwas vereinfachen.#define PROPERTY_INIT(name) name(#name, catalog_) FooBar::FooBar() : catalog_("FooBar", *this) , PROPERTY_INIT(aProperty) , PROPERTY_INIT(anotherProperty) { }
@ phreck: Wie würdest du denn den Mechanismus bezeichnen, wenn nicht Reflection/Introspection? Dass man jedes Mal zwei Sätze zur Beschreibung braucht, nur damit der Leser sich denkt "ah, er meint wohl Reflection", kanns ja auch nicht sein.
-
@ phreck: Wie würdest du denn den Mechanismus bezeichnen, wenn nicht Reflection/Introspection? Dass man jedes Mal zwei Sätze zur Beschreibung braucht, nur damit der Leser sich denkt "ah, er meint wohl Reflection", kanns ja auch nicht sein.
Oh, da hast du was falsch verstanden, wahrscheinlich mein Fehler.
Ich bin ja gerade für die Bezeichnung Reflections oder Reflexion, wohl aber mit der Bemerkung, dass C++ hier builtin nur ein Subset unterstützt
-
Nein, ich meinte Reflection im Java-Sinne (als Abgrenzung von den Mechanismen, die C++ kann). Also dass man Klassen zur Laufzeit abfragen kann, was sie für Methoden besitzen etc. Und zwar nativ, ohne Precompiler oder so. Welchen Begriff soll man dafür benutzen, wenn nicht Reflection ("C++ kann kein ...")?
(Sorry für OffTopic)
-
Student83 schrieb:
Den Eindruck habe ich auch, aber die Library soll nunmal plattformunabhängig und mit OpenGL verwendet werden können. Da komme ich an C++ nicht vorbei.
Nach meiner bisherigen Erfahrung kann ich dir folgendes raten: Tu dir selbst und anderen Maintainern einen Gefallen und bleibe bei std::map<>.
C++ läßt sich durch TMP und Makroorgien zu allerhand lustigen Dingen zwingen, die eigentlich nicht vorgesehen waren, aber das macht den Umgang mit dem Code meist nicht einfacher. Wenn du ein Makrosystem für die Definition von Properties und die Generation von RTTI einführst, erstellst du zwangsweise eine Art "Domain-specific dialect"; wer deinen Code verstehen oder gar erweitern will, muß sich erst gut mit deiner "Makrosprache" auskennen, was natürlich erfordert, daß du sie entsprechend gut dokumentierst. Insbesondere das Debuggen von Code, der viel mit Makros arbeitet, verläuft oft sehr gegen die Intuition; der Debugger springt dauernd in irgendwelchen Makroaufrufen umher, und du kannst nicht nachvollziehen, was passiert. Auch die Compilerfehler bei der Fehlbenutzung eines Makros sind meist völlig irreführend.
All diese Probleme hast du bei std::map<> nicht. Wenn du unbedingt automatische Persistenz haben möchtest, dann nimm eine Sprache, die Reflection unterstützt, wie z.B. C#, Delphi oder Java.
-
Wie meinst du das mit std::map<>? Das mit den Templates habe ich bis jetzt auch zu vermeiden versucht, denn das macht das Debuggen wirklich nervig. Das Problem ist, dass für meinen Zweck nur C++ in Frage kommt. Die Nachteile der anderen Sprachen sind einfach schwerwiegender.