Generische Getter Funktion für Struct



  • Shade Of Mine schrieb:

    das ganze wirkt aber ziemlich frickelig... kannst du denn nicht zB die structs direkt aus der dll exportieren?

    Nein, das geht meines Wissens nach nicht.

    Ich möchte einfach meinen Getter aufrufen und den gewünschten Wert geliefert bekommen. Nur möchte ich eben nicht für jeden Struct neue Getter schreiben oder alle Getter anfassen, wenn die Member eh die gleichen sind.


  • Mod

    Wenn alle Namen nochmal als externe Konstanten vorliegen dürfen, könnte man evtl. so etwas machen:

    template <const char* S, typename T>
    struct get_member;
    
    template <const char* S, typename T>
    int template_get_int(const T& x)
    {
        return get_membert< S, T >()( x );
    } 
    #if DEFINE_STRINGS_HERE
    #define STRING_DEF( x ) extern const char member_name_##x[] = #x;
    #else
    #define STRING_DEF( x ) extern const char member_name_##x[];
    #endif
    #define MEMBER_GET_DEF( x ) \
      STRING_DEF( x ) \
      template <typename T> struct get_member< member_name_##x, T > { int operator()(const T& o) const { return o.##x; } };
    
    MEMBER_GET_DEF( a );
    MEMBER_GET_DEF( b );
    MEMBER_GET_DEF( c );
    MEMBER_GET_DEF( d );
    
    struct{
    int a;
    int b;
    int c;
    }s1;
    s1 x;
    template_get_int<member_name_a>(x);
    

    ist aber erst mal nur als Idee zu verstehen. In C++0x würde man evtl. mit selbst definierten Literalen arbeiten können. Da diese meines Wissens bisher durch keinen Compiler implementiert werden, ist das allerdings erst mal nur Theorie.



  • Und Funktionsüberladung? Oder brauchst du ein C-Interface (dann ginge aber auch das Template nicht)?

    Mit Boost.Preprocessor könntest du dir Makros schreiben, die für eine bestimmte Struktur eine Funktion generieren.

    #define GET_INT(STRUCT, MEMBERLIST) ...
    

    Aber eins kann ich dir jetzt sagen: Schön wird das nicht, falls es überhaupt gehen sollte...



  • camper schrieb:

    Wenn alle Namen nochmal als externe Konstanten vorliegen dürfen, könnte man evtl. so etwas machen:

    template <const char* S, typename T>
    struct get_member;
    
    template <const char* S, typename T>
    int template_get_int(const T& x)
    {
        return get_membert< S, T >()( x );
    } 
    #if DEFINE_STRINGS_HERE
    #define STRING_DEF( x ) extern const char member_name_##x[] = #x;
    #else
    #define STRING_DEF( x ) extern const char member_name_##x[];
    #endif
    #define MEMBER_GET_DEF( x ) \
      STRING_DEF( x ) \
      template <typename T> struct get_member< member_name_##x, T > { int operator()(const T& o) const { return o.##x; } };
    
    MEMBER_GET_DEF( a );
    MEMBER_GET_DEF( b );
    MEMBER_GET_DEF( c );
    MEMBER_GET_DEF( d );
    
    struct{
    int a;
    int b;
    int c;
    }s1;
    s1 x;
    template_get_int<member_name_a>(x);
    

    ist aber erst mal nur als Idee zu verstehen. In C++0x würde man evtl. mit selbst definierten Literalen arbeiten können. Da diese meines Wissens bisher durch keinen Compiler implementiert werden, ist das allerdings erst mal nur Theorie.

    Ehrlich gesagt, verstehe ich den Code nicht so ganz...

    Wenn ich jetzt noch ein andere Struct habe, z.B. ohne das 'a', dann schlägt er doch wieder auf oder? Und zwar bei MEMBER_GET_DEF( a ); ?

    Ne andere Idee, die mir gerade noch gekommen ist. Kann man nicht einfach ein Struct in eine Map exportieren? Also dass er automatisch den Member Namen als Key nimmt.



  • Nexus schrieb:

    Und Funktionsüberladung? Oder brauchst du ein C-Interface (dann ginge aber auch das Template nicht)?

    Mit Boost.Preprocessor könntest du dir Makros schreiben, die für eine bestimmte Struktur eine Funktion generieren.

    #define GET_INT(STRUCT, MEMBERLIST) ...
    

    Aber eins kann ich dir jetzt sagen: Schön wird das nicht, falls es überhaupt gehen sollte...

    Nein, es kann ruhig C++ sein. Funktionsüberladung bringt ja nichts, weil ich dann ja für jeden Struct wieder eine Überladung schreiben müsste.

    Das mit den Macros würde in der Tat sehr unschön und unübersichtlich werden.

    Es muss doch ne einfache Lösung für das Problem geben... 😞



  • defox schrieb:

    Es muss doch ne einfache Lösung für das Problem geben... 😞

    ein python script oder sonstwas dass dir den code anhand der struct definitionen generiert. ist leicht wartbar, schön und übersichtlich. IMHO viel besser als makro oder template orgien.



  • Es gibt auch irgendeinen kranken Template-Trick, mit dem man rausbekommen kann, ob eine Klasse ein Member mit einem bestimmten Namen hat. Ich habe aber leider keinen Link dazu parat.

    Wenn man mit MSVC arbeitet, kann dazu vermutlich die "__if_exists" Extension verwenden:
    http://msdn.microsoft.com/en-us/library/x7wy9xh3(VS.80).aspx
    Ist dann natürlich überhaupt nicht mehr kompatibel zu irgendwas ausser MSVC.


  • Mod

    defox schrieb:

    Ehrlich gesagt, verstehe ich den Code nicht so ganz...

    Wenn ich jetzt noch ein andere Struct habe, z.B. ohne das 'a', dann schlägt er doch wieder auf oder? Und zwar bei MEMBER_GET_DEF( a ); ?

    MEMBER_GET_DEF definiert nur eine geeignete partielle Spezialisierung, einen Fehler kann es erst beim Instantiieren, d.h. dem eigentlichen Funktionsaufruf geben.

    Allerdings ist mir eine Vereinfachung eingefallen, die die Definition zusätzlicher Globaler Variablen überflüssig macht.

    #ifndef MEMBERGET_HPP
    #define MEMBERGET_HPP
    
    namespace // annonymer Namensraum, wegen __LINE__-Makro
              // führt allerdings dazu, dass man theoretisch auf inline-Funktionen, die hiervon Gebrauch machen, verzichten muss
    {
    template <int memberid, typename T>
    struct get_member_helper;
    
    #define DEFINE_MEMBER_GETTER( name ) \
    const int member_id_##name = __LINE__; \
    template <typename T> \
    struct get_member_helper<member_id_##name,T> \
    { \
        int operator()(const T& o) const { return o.name; } \
    };
    
    template <int id, typename T>
    int get_member(const T& o) { return get_member_helper<id,T>()(o); }
    
    #define GET_MEMBER( name ) get_member<member_id_##name>
    
    // DEFINE für jeden möglichen Namen
    DEFINE_MEMBER_GETTER( a )
    DEFINE_MEMBER_GETTER( b )
    DEFINE_MEMBER_GETTER( c )
    }
    #endif
    
    ...
    foo x;
    GET_MEMBER(a)(x)  // x.a
    GET_MEMBER(c)(x)  // x.c
    


  • So.
    Compile-Time Member-Detection ist hier beschrieben:
    http://www.codeproject.com/KB/architecture/Detector.aspx

    Geht aber nur, wenn man den Typ des Members kennt.



  • So, mein Problem ist gelöst.
    Ich habe einfach mein Template um die "__if_exists" Extension erweitert und das ganze etwas mit ein paar Makros verkürzt.

    #define GET_INT(n) if (strcmp(name, #n) == 0) { __if_exists(T1::n) { return a.n; } }
    
    struct{
    int a;
    int b;
    int c;
    }s1;
    
    struct{
    int b;
    int c;
    int d;
    }s2;
    
    template <class T1>
    inline int template_get_int(T1 s, char * name)
    {
        GET_INT(a)
        GET_INT(b)
        GET_INT(c)
        GET_INT(d)
        return 0;
    }
    

    So brauche ich nur für jeden neuen Member eine Makrozeile einfügen.
    Vielen Dank für die Hilfe.


Anmelden zum Antworten