Generische Getter Funktion für Struct



  • Hallo,

    ich habe folgendes Problem:

    - Ich bekomme mehrere Structs, die sich sehr ähneln, d.h. viele Member sind identisch. Einige Structs haben allerdings das eine oder andere Feld mehr oder weniger. Auf diese Structs habe ich keinen Einfluss!

    - Ich benötige Getter Funktionen für die einzelnen Member der Structs.

    - Da öfters neue Structs hinzukommen, soll der Code einfach zu erweitern sein. Ich möchte nur Änderungen am Code durchführen, wenn neue Member hinzugefügt werden. Ich möchte also NICHT für jeden Struct eigene Getter Funktionen haben, sondern generische.

    Mein Lösungsansatz sieht im Moment wie folgt aus:

    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)
    {
    	if (name == "a") return s.a;
    	else if (name == "b") return s.b;
    	else if (name == "c") return s.c;
    	else if (name == "d") return s.d;
    	return 0;
    }
    

    Ich möchte also der Template Funktion das Struct übergeben und den Namen des Members, zurück soll dann der Wert kommen.

    Leider funktioniert das ganze so natürlich nicht. Der Compiler findet ja für das s1 Struct kein Member d und für s2 kein Member a.

    Gibt es eine Möglichkeit dem Compiler mitzuteilen, dass er einfach eine 0 einsetzen soll, falls das Element nicht existiert?

    Oder gibt es eine andere gute Möglichkeit das ganze zu realisieren?

    Verwendet wird der Visual Studio 2005 Compiler.

    Besten Dank im voraus!



  • Für was brauchst du da einen getter?



  • Das ganze kommt in eine DLL, die extern aufgerufen wird (von Agilent Vee).
    Daher benötige ich eine Funktion, die mir den entsprechenden Wert liefert.



  • defox schrieb:

    Das ganze kommt in eine DLL, die extern aufgerufen wird (von Agilent Vee).
    Daher benötige ich eine Funktion, die mir den entsprechenden Wert liefert.

    in python einen parser schreiben der dir c code ausspuckt.

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



  • 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