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 ändernjetzt 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-parameterGetter 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 kannmit 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 machenstruct 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 == boolAnwendung:
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 aufrufenich 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
-
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 haben3. 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?
-
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 habenWenn 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
-
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