Indexed Property System



  • 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.



  • Gerade die Templates sind doch, besonderst die STL, das wichtigste an C++. Das ist ja wie fahren eines Autos ohne Motor nur mit Pedalen... Und ich sehe nicht warum das Nutzen der STL das Debuggen schwerer macht??? Ganz im Gegenteil, es nimmt dir die Arbeit ab diese Elemente selbst erstellen zu müssen.



  • Student83, was ist eigentlich mit phrecks Vorschlag?


Anmelden zum Antworten