Set und Get-Methode in pseudo Propert umwandeln



  • ich habe mehrere Klasse mit "Properties" die Klassen darf ich definitiv nicht ändern - ich soll nur Mapping-Code von/zu verschiedenen Systemen schreiben - und dafür muss ich ständig die einzelnen Methoden aufrufen, kann nichts templatisieren usw.

    so sehen die Klassen aus

    class A
    {
    public:
      void SetX(int x){ _x = x; }
      int GetX(){ return _x; }
    
      void SetY(int y){ _y = y; }
      int GetY(){ return _y; }
      //...
    
    private:
      int _x = 0;
      bool _y = false;
      //...
    }
    

    was mir klar ist:
    -trivial Getter/Setter sind meistens nicht sinnvoll - ich darf das aber nicht ändern
    -man koennte ein Property-Template oder sowas machen - darf ich auch nicht
    -wie gesagt ich darf nichts ändern

    jetzt wollte ich mir ein Template mache mit der ich das get/set vereinheitlichen kann

    so in der Form:
    habe mich daran versucht zu orientieren:
    https://stackoverflow.com/questions/7557990/c-method-name-as-template-parameter

    Getter ist immer: type GetXYZ()
    Setter ist immer: SetXYZ(type)

    struct data
    {
      int get(){ return _x; }
      void set(int value){ _x = value; }
      int _x = 0;
    };
    
    template<typename Data, typename Getter, typename Setter>
    struct getset_mapper
    {
        //wie komme ich an den return-type vom Getter? max c++11
        //typedef getter_result = decltype(Getter); //
    
        void get(const Data& data) {
            //auto x = (data.*Getter)();
        }
    
        void set(Data& data) {
            //(data.*Setter(321));
        }
    };
    
    int main(void)
    {
      data a;
      data b;
    
      getset_mapper<data, &data::get, &data::set> gsm;
      //gsm_type::getter_type == int
      gsm.get(a); // sollte data(a)::get() => 0 aufrufen
      a._x = 321;
      gsm.get(a); // sollte data(a)::get() => 321 aufrufen
      gsm.get(b); // sollte data(a)::get() => 0 aufrufen
      gsm.set(b); // sollte data(b)::set(123) aufrufen
    }
    

    ich kann max. C++11 verwenden

    Jemand eine nette Idee



  • Zusatzinfo: ich brauche den Scopen von struct getset_mapper weil ich noch ein paar Members für das mapping brauche



  • Was genau willst du jetzt machen?
    Bist du bockig, weil dir die coding-standards deines Arbeitgebers nicht passen und willst da jetzt irgendwas basteln, um die zu umgehen?



  • Was genau willst du jetzt machen?

    im Grunde will ich ein Template haben mit dem ich
    getrennte Set/Get Methoden wieder zusammenfuehren kann

    mit diesen könnte ich dann lokal in meinem Code viel leichter
    Transfer-Code von System A in System B schreiben - weil ich
    mit hilfe dieses GetSet-Templates weiter templatisieren kann, erst
    die Templates die ich auf dem GetSet Template basieren lasse werden die Arbeit
    erleichtern und fehlerfreier machen

    struct data // von dieser Klassen-Art gibt es 20-30 Stück
    {
    int getX(){ return _x; }
    void setX(int value){ _x = value; }

    bool getY(){ return _y; }
    void setY(bool value){ _y = value; }

    int _x = 0;
    bool _y = false;
    };

    //Set/Get Member Zuordnung/Aufbau
    getset<data, data::getX, data::setX> dataX
    getset<data, data::getX, data::setX>::type_of == int
    getset<data, data::getY, data::setY> dataY
    getset<data, data::getY, data::setY>::type_of == bool

    Anwendung:

    data a;
    data b;

    dataX.get(a) => ruft a.getX() auf
    dataY.get(b) => ruft b.getY() auf
    usw.

    damit kann ich dann z.B. eine Beschreibung machen
    und mit einem anderen Code entweder alle Getter oder Setter aufrufen

    ich hab nur noch nie mit Member-Function-Pointer gespielt - oder ist lange her

    Bist du bockig, weil dir die coding-standards deines Arbeitgebers nicht passen und willst da jetzt irgendwas basteln, um die zu umgehen?

    wie kommst du darauf - es einfach viel Legacy-Code der an ein anderes System abgebunden werden muss und ich darf den Original-Code nicht ändern - thats it
    ich möchte mich nicht für 20-30 Klassen die nicht gut aufgebaut sind nochmal eine riesen Menge sinnlosen Code reinbringen - deswegen mein Ansatz


  • Mod

    partielle Spezialsierung kann helfen

    #include <utility>
    
    struct data
    {
      int get() const { return _x; }
      void set(int value){ _x = value; }
      int _x = 0;
    };
    
    template<typename Data, typename GetterType, GetterType Getter, typename SetterType, SetterType Setter>
    struct getset_mapper;
    template<typename Data, typename GR, typename... GArgs, GR(Data::*Getter)(GArgs...) const, typename SR, typename... SArgs, SR(Data::*Setter)(SArgs...)>
    struct getset_mapper<Data, GR(Data::*)(GArgs...) const, Getter, SR(Data::*)(SArgs...), Setter>
    {
        using getter_result = GR;
        using setter_result = SR;
    
        template <typename... Args>
        static getter_result get(const Data& data, Args&&... args) {
            return (data.*Getter)(std::forward<Args>(args)...);
        }
    
        template <typename... Args>
        static setter_result set(Data& data, Args&&... args) {
            return (data.*Setter)(std::forward<Args>(args)...);
        }
    };
    
    int main(void)
    {
      data a;
      data b;
    
      getset_mapper<data, decltype(&data::get), &data::get, decltype(&data::set), &data::set> gsm;
    // evtl. Makro:
    #define GET_SET_MAPPER(class, get, set) getset_mapper<class, decltype(&class::get), &class::get, decltype(&class:set), &class::set>
    // in C++17 dann besser mit auto Templataparameter
      //gsm_type::getter_type == int
      gsm.get(a); // sollte data(a)::get() => 0 aufrufen
      a._x = 321;
      gsm.get(a); // sollte data(a)::get() => 321 aufrufen
      gsm.get(b); // sollte data(a)::get() => 0 aufrufen
      gsm.set(b,123); // sollte data(b)::set(123) aufrufen
    }
    


  • Danke schon mal - läuft noch nicht out-of-the-box aber man soll ja nicht zu viel verlangen 🙂

    Fragen:

    1. brauch man die offenen Parametergrenzen (GArgs..., SArgs...) auch
    wenn klar ist das der Getter keine Parameter haben darf und der Setter nur einen Parameter vom Typ den der Getter liefert?
    und kann man sich dann die std::forwards sparen?

    2. muss der return beim set auch dann stehen wenn alle Setter
    keinen Rückgabewert haben

    3. kann man das decltype(&data::get) auch in das Template verschieben - oder einen Wrapper - damit man die Angabe nicht zwei mal machen muss? Keine Möglichkeit um das Macro (zu vereinfachung) zu kommen?


  • Mod

    Gast3 schrieb:

    1. brauch man die offenen Parametergrenzen (GArgs..., SArgs...) auch
    wenn klar ist das der Getter keine Parameter haben darf und der Setter nur einen Parameter vom Typ den der Getter liefert?
    und kann man sich dann die std::forwards sparen?

    selbstverständlich. Ohne forward hast nur evtl. eine überflüssige Kopie.

    Gast3 schrieb:

    2. muss der return beim set auch dann stehen wenn alle Setter
    keinen Rückgabewert haben

    Wenn der Rückgabetyp stets cv void ist, brauchst du kein return - es stört aber auch nicht.

    Gast3 schrieb:

    3. kann man das decltype(&data::get) auch in das Template verschieben - oder einen Wrapper - damit man die Angabe nicht zwei mal machen muss? Keine Möglichkeit um das Macro (zu vereinfachung) zu kommen?

    Nur ab C++17 -> auto



  • Danke für deine Tips - ich denke ich muss jetzt selber erst mal probieren


  • Mod

    Mit C++17 kann das dann so aussehen:

    #include <utility>
    
    struct data
    {
        int get() const { return _x; }
        void set(int value) { _x = value; }
        int _x = 0;
    };
    
    template <auto Getter, auto Setter>
    struct getset_mapper
    {
        template <typename C, typename... Args>
        static decltype(auto) get(C&& data, Args&&... args) noexcept(noexcept((std::forward<C>(data).*Getter)(std::forward<Args>(args)...))) {
            return (std::forward<C>(data).*Getter)(std::forward<Args>(args)...);
        }
    
        template <typename C, typename... Args>
        static decltype(auto) set(C&& data, Args&&... args) noexcept(noexcept((std::forward<C>(data).*Setter)(std::forward<Args>(args)...))) {
            return (std::forward<C>(data).*Setter)(std::forward<Args>(args)...);
        }
    };
    
    #include <iostream>
    int main()
    {
      data a;
      data b;
    
      getset_mapper<&data::get, &data::set> gsm;
      std::cout << gsm.get(a) << '\n';
      a._x = 321;
      std::cout << gsm.get(a) << '\n';
      std::cout << gsm.get(b) << '\n';
      gsm.set(b, 123);
      std::cout << gsm.get(b) << '\n';
    }
    


  • Mit C++17 kann das dann so aussehen:

    die schöne neue Welt von C++17 🙂

    Danke


Anmelden zum Antworten