Benennung von "Accessors"
-
Decimad schrieb:
An Jockel gesondert: Wie genau kommt es, dass du solche Situation noch nicht hattest? Ich kann mir das gar nicht vorstellen!
Wahrscheinlich meinte er, dass ihm 0815-Setter und -Getter bisher meist gereicht haben. Das geht mir ähnlich. Ich hab zwar auch schon Artikel gelesen, die Setter/Getter verdammen und eine Alternative (war glaub ich was mit Funktoren) heilig sprechen. Aber so wirklich hat mich das bisher nie überzeugt.
Decimad schrieb:
Schade nur, dass ich selbst für den Direktmodus im Moment keinen Weg sehe, das ohne Indirektion (sei es über Zeiger auf size_ oder sonstwie) umzusetzen.
Wie wärs, wenn du die Membervariable direkt öffentlich machst?
template <typename T> class Accessor { public: // Schnittstelle, um Property zu simulieren // d.h. Zuweisungs- und Konvertierungsoperator etc. private: T value; }; class Widget { public: Accessor<vis::Size> Size; private: // nichts };
-
Bam, na klar, für den Direktmodus habe ich da wieder zu kompliziert gedacht, danke dass du mich da auf den Boden zurückgeholt hast, hehehe.
Allerdings ist es ja nun so, dass z.B. die Veränderung der Ausdehnung eines Widgets natürlich größere Auswirkungen auf den Rest haben (das ist ja der interessante Punkt und überhaupt der Hauptgrund um die Member, so sie denn in der direkten Form existieren, zu verstecken) und der Direktmodus damit ausfällt. In dem Fall würde man ja mit some.Size erstmal den Kontext "weg-punkten" und damit muss Size den Kontext intern gespeichert haben, beispielsweise durch einen Zeiger auf das beinhaltende Objekt.
-
Decimad schrieb:
Allerdings ist es ja nun so, dass z.B. die Veränderung der Ausdehnung eines Widgets natürlich größere Auswirkungen auf den Rest haben (das ist ja der interessante Punkt und überhaupt der Hauptgrund um die Member, so sie denn in der direkten Form existieren, zu verstecken) und der Direktmodus damit ausfällt.
Du kannst ja dem
AccessorFunktionalität mitgeben, falls nötig. Z.B. mitstd::tr1::function, aber keine Ahnung, ob das drin liegt. DaAccessorein Template ist, könntest du unter Umständen sogar (Member-)Funktionszeiger als Template-Parameter fest verdrahten.Diese Properties in C++ sind jedenfalls ein interessantes Experiment

Wie würdest du denn umgekehrt die Funktionalität einbauen, wenn der Accessor nur eine Indirektion auf den Member wäre? Und was stört dich dabei an den Zeigern?
-
Nexus schrieb:
Decimad schrieb:
An Jockel gesondert: Wie genau kommt es, dass du solche Situation noch nicht hattest? Ich kann mir das gar nicht vorstellen!
Wahrscheinlich meinte er, dass ihm 0815-Setter und -Getter bisher meist gereicht haben.
Ja. Ich finde diese C#-Properties zwar sehr nett, aber da das in C++ so nicht vorgesehen ist, versuche ich das nicht irgendwie nachzubauen.
Also sind meine getter/setter normale Funktionen und die tuen was und sind daher Verben: getXY/setXY.
(Ich dachte diese Variante dürfte man aber nicht nennen, da du die nicht haben willst).
-
Ganz im Gegenteil, Jockel. Das ist doch eine ziemliche Geschmackssache und niemand kann einem sagen, dass einem das schönste Paar Schuhe aus Geschäft 1 noch gefällt, wenn man in ihnen vor Geschäft 2 steht. Darum freue ich mich über jede Ansicht, derer ich in diesem Belang habhaft werden kann!
An Nexus: Ich werde nun am besten erstmal schlafen gehen, aber werde morgen mal mit der Idee etwas rumspielen und mich wieder melden! Danke erstmal!
-
Wenn du an unterschiedlichen Möglichkeiten interessiert bist, dann noch meine:
namespace vis { class Size { public: const int y() const { return y_; } int& y() { return y_; } private: int y; }; class Widget { public: const Size& size() const { return size_; } Size& size() { return size_; } private: Size size_; }; void test() { Widget widget; widget.size().y() += 10; // sowas ist doch einfach schön const Widget w2(widget); int y = w2.size().y(); // nur read-only Zugriff } }
-
alternato schrieb:
widget.size().y() += 10; // sowas ist doch einfach schönIch habe eindeutig ein anderes Schönheitsempfinden

In C++ bin ich ebenfalls für set und get und zwar nach dem Codestil der Standardbibliothek:
get_sizeundset_size. Wobei ich mir auch für reine Getter einsizevorstellen könnte. Als Setter sieht eine Funktionsizeaber einfach nur blöd aus.Implementierungen für Properties unter C++ gibt es im Netz schon zahlreiche. Allerdings kann es schnell zu gewissen Unschönheiten und sogar Problemen kommen. C++ hat diese Möglichkeit einfach nicht und sich diese Möglichkeit irgendwie hinzubiegen, halte ich für verkehrt.
Grüssli
-
Hier übrigens noch ein alter Thread, der schon eine ähnliche Frage zum Thema hatte: Link
Und hier der Artikel, den ich vorhin erwähnt habe, der jedoch nur eine begrenzt bessere Alternative zu Settern/Gettern bietet: Link
-
alternato schrieb:
Wenn du an unterschiedlichen Möglichkeiten interessiert bist, dann noch meine:
// [Code-Auszug] class Widget { public: const Size& size() const { return size_; } Size& size() { return size_; } private: Size size_; };Und wo genau ist hier der Vorteil gegenüber öffentlichen Memberfunktionen?
- Bei veränderbaren Objekten gibst du eine Non-Const-Referenz auf die Variable zurück. Damit hat man den gleichen Zugriff wie auf die Variable selbst.
- Bei konstanten Objekten gibst du eine Const-Referenz oder Kopie zurück. Dadurch kann man die Variable zwar nicht verändern, aber genau das Gleiche trifft auf eine öffentliche Membervariable zu.
Der Unterschied zu
public-Variablen sind also lediglich die Klammern beim Aufruf und der zusätzliche Code, was ich nicht unbedingt als Vorteil werten würde.
-
Hey, danke Nexus für's rauskramen, leider wurde der Thread irgendwie erst interessant mit dem letzten Beitrag, weil sich die Diskussion aus irgendwelchen Gründen vorher eher darum dreht, ob man denn überhaupt accessor-Methoden einsetzen sollte... Der andere Artikel war interessant zu lesen.
-
Dravere schrieb:
Implementierungen für Properties unter C++ gibt es im Netz schon zahlreiche. Allerdings kann es schnell zu gewissen Unschönheiten und sogar Problemen kommen. C++ hat diese Möglichkeit einfach nicht und sich diese Möglichkeit irgendwie hinzubiegen, halte ich für verkehrt.
Also ohne eingehende Begründung (von der ich fest ausgehe, dass du sie besitzt, nur nicht so weit ausholen wolltest) finde ich die Argumentation "C++" hätte etwas nicht eingabaut -> versuch es nicht trotzdem, wenig überzeugend

-
Nexus schrieb:
Der Unterschied zu
public-Variablen sind also lediglich die Klammern beim Aufruf und der zusätzliche Code, was ich nicht unbedingt als Vorteil werten würde.Es hat einige zusätzliche Vorteile gegenüber
public-Variablen:- Du kannst die Size irgendwo speichern. In einem
std::vector, in einerunion, in einempImpl, usw., ohne dass der Aufrufer davon wissen muss. - Du hast die Möglichkeit, nicht direkt eine Referenz sondern einen Acessor, der sich so verhält wie eine Size, zurückzugeben. Im Normalfall ist das allerdings nicht nötig.
- Du kannst den Zugriff loggen.
- Einheitlichkeit. Ein öffentlich unveränderlicher Member kann nicht als öffentliche Variable definiert werden, sondern muss wie von der STL von
vector::size()als Funktion zur Verfügung stehen. Einmal mit, einmal ohne Klammern - immer mit sehe ich da als Vorteil.
Nexus schrieb:
Und wo genau ist hier der Vorteil gegenüber öffentlichen Memberfunktionen?
- Einheitlichkeit. Ob ein Member unveränderlich ist wie ein
vector::size()oder eben doch veränderlich, der Zugriff bleibt gleich. - Für manche Leute subjektiv schönerer Code (
a.foo() += 5;). - Objektiv kürzerer Code.
Muss dich natürlich nicht überzeugen, Boost und mein Lieblingstoolkit verwenden auch die
get_prop/set_prop-Variante, das zwingt mich dann doch zu solchen Eigenschaftsfunktionen in allen damit verbundenen Klassen. Ich bin mit meinem Geschmack scheinbar in der Unterzahl.
- Du kannst die Size irgendwo speichern. In einem
-
C++ hat ja einiges nicht eingebaut. Aber wir haben Templates, um alles nachzubauen

Ich hab teilweise schon echt interessante Ansätze gesehen, z.B. für Reflection, Multiple Dispatch, Lambda-Funktionen, benannte Argumentlisten, ...Naja, wird wohl etwas OffTopic. Decimad, hast du schon etwas mit dem
Accessorherumexperimentiert? Du hast ja gesagt, du sähest die Möglichkeit über Zeiger. Wie würde das konkret aussehen, Funktionalität zu implementieren? Auch über Callables (std::tr1::function)?
-
Ruhig Blut junger Padawan

Also in der simpelsten Form würde ich auf die generalisierten Funktionszeiger ala tr1::function gerne erstmal verzichten, da es sich ja erstmal nicht um dynamische Beziehungen handelt, sondern um zur Compile-Zeit festgelegte (von denen man einige in die Laufzeit rüberschleppen muss, weil C++ eben diese Funktionalität nicht eingebaut hat). Man könnte sich natürlich in Form einer weiteren Generalisierung über Verkettungen Gedanken machen, um beispielsweise dynamische Restriktionen der Properties zu implementieren...
-
alternato schrieb:
Du kannst die Size irgendwo speichern. In einem
std::vector, in einerunion, in einempImpl, usw., ohne dass der Aufrufer davon wissen muss.Okay, das stimmt.
Du hast die Möglichkeit, nicht direkt eine Referenz sondern einen Acessor, der sich so verhält wie eine Size, zurückzugeben.
Womit wir wieder beim Property wären...
alternato schrieb:
Du kannst den Zugriff loggen.
Aber nur beschränkt. Du kannst nur aufzeichnen, wann die Referenz zurückgegeben wurde, und nicht jede einzelne Veränderung. Du siehst auch nicht, welche Art von Veränderung stattfindet. Das ist ein Nachteil gegenüber
SetPosition(absolutePos)undMove(offset), weil du=und+=nicht unterscheiden kannst.Du kannst aber in der Dokumentation schreiben, dass die Referenz nicht weiterverwendet werden soll, sodass du zumindest den Zeitpunkt kennst. Auch wenn du damit ein paar sinnvolle Fälle einschränkst.
alternato schrieb:
Einheitlichkeit. Ob ein Member unveränderlich ist wie ein
vector::size()oder eben doch veränderlich, der Zugriff bleibt gleich.Was aber auf
GetSize()ebenso zutrifft.alternato schrieb:
Objektiv kürzerer Code.
Das kommt erst bei komplexeren Ausdrücken zum Tragen, wie bei
obj.SetSize(5 * obj.GetSize()); // vs obj.Size() *= 5;Dafür nimmst du eine geringere Kontrollmöglichkeit in Kauf (siehe oben). Falls dieser spezifische Fall oft vorkommt, könnte man sich auch sowas überlegen:
obj.Scale(5);alternato schrieb:
Muss dich natürlich nicht überzeugen [...]. Ich bin mit meinem Geschmack scheinbar in der Unterzahl.
Ich bin nur bisher meist mit konventionellen Get/Set-Methoden zufrieden gewesen. Ab und zu habe ich auch Strukturen mit nur öffentlichen Variablen.
Aber wenn, dann denke ich am ehesten sowas wie den
Accessoraus diesem Thread. Diese Alternative könnte durchaus in manchen Fällen Potenzial haben. Und zufälligerweise stehe ich auch gleich vor einem ähnlichen Problem (einer Klasse mit sehr vielen Get/Set-Funktionen).
-
Nach diesem Motto hatte ich mir das vorgestellt: (Man beachte die nicht so tolle Konstruktion, this vor dem Betreten des Konstruktors zu verwenden...)
template< typename DataType, typename Class, void (Class::*Setter)( const DataType& ), DataType (Class::*Getter)() const > class Accessor { public: Accessor( Class* p ) : this_(p) {} Accessor& operator=( const DataType& data ) { (this_->*Setter)( data ); return *this; } private: Class* this_; }; class Test { public: Test() : Value( this ) {} private: void setValue( const int& val ) { std::cout << "Setting to " << val; } int getValue() const; public: Accessor< int, Test, &Test::setValue, &Test::getValue > Value; }; int main() { Test myTest; myTest.Value = 10; return 0; }
-
Decimad schrieb:
Dravere schrieb:
Implementierungen für Properties unter C++ gibt es im Netz schon zahlreiche. Allerdings kann es schnell zu gewissen Unschönheiten und sogar Problemen kommen. C++ hat diese Möglichkeit einfach nicht und sich diese Möglichkeit irgendwie hinzubiegen, halte ich für verkehrt.
Also ohne eingehende Begründung (von der ich fest ausgehe, dass du sie besitzt, nur nicht so weit ausholen wolltest) finde ich die Argumentation "C++" hätte etwas nicht eingabaut -> versuch es nicht trotzdem, wenig überzeugend

Gut, dann bringe ich mal ein einfaches Argument:
Man verwendet Getter und Setter unterandem, damit zusätzlicher Code bei der jeweiligen Aktion durchgeführt werden kann. Auch wenn es nur ein Breakpoint ist oder man die Aktion protokollieren möchte. Wie erreichst du dies mit den nachgebauten Properties? Geht nicht! Es erfordert gleich die Abänderung der Property-Hilfsklasse und betrifft somit alle Properties, statt nur eines einzelnen. Oder dann muss man einen Callback-Mechanismus einbauen, welcher aber recht aufwendig sein kann.std::tr1::functionoderboost::functionsind unheimlich komplexe Gebilde und kosten auch einiges an Zeit. Wenn man anfängt für so Dinge wie Setter und Getter viel Zeit zu verbraten, nur weil man dadurch 3 Zeichen einsparen kann, dann ist das irgendwie recht fraglich.
Wenn du die Properties in C# ansiehst, dann kannst du dort ganz einfach die Getter und Setter eines jeden Properties an deine Bedürfnisse anpassen, ohne zusätzliche Laufzeit zu verbraten.Es ist alles eben ein wenig eine Frage des Preis-Leistungsverhältnis. Was bringen einem Properties in C++? Das einzige Argument ist, welches ich bisher gesehen habe, dass man nicht mehr
size()schreiben muss sondernsize. Es geht also darum zwei Klammern einzusparen. Damit soll angeblich die Lesbarkeit erhöht werden. Dass man auch mit temporären Zwischenvariablen arbeiten kann, wird hingegen oft ignoriert:int x = pos.get_x(); int y = pos.get_y(); int width = rect.get_width(); int index = y * width + x;Was soll daran unübersichtlich sein?
Ein anderes bisschen einfacheres Argument:
Wie machst du Properties kopierbar, aber von aussen nicht zuweisbar?Properties Workarounds in C++ bieten keinerlei relevanten Vorteil. Dafür können sie Nachteile mit sich bringen. Wieso sollte man also eine Technik einsetzen, welche unter dem Strich nichts bringt?
Grüssli
-
Decimad schrieb:
Also in der simpelsten Form würde ich auf die generalisierten Funktionszeiger ala tr1::function gerne erstmal verzichten, da es sich ja erstmal nicht um dynamische Beziehungen handelt, sondern um zur Compile-Zeit festgelegte
Genau, deshalb auch noch der Vorschlag "Da Accessor ein Template ist, könntest du unter Umständen sogar (Member-)Funktionszeiger als Template-Parameter fest verdrahten." Scheinbar hast du das auch gerade getan

Mit Metaprogrammierung könnte man wahrscheinlich noch etwas optimieren, sodass z.B.
intstattconst int&verwendet werden kann.Decimad schrieb:
Man beachte die nicht so tolle Konstruktion, this vor dem Betreten des Konstruktors zu verwenden...
Ist das ein Problem? Du verwendest ja nur den Zeiger als Verweis. Soweit ich weiss, wird es erst beim tatsächlichen Zugriff auf
thisproblematisch...
-
Ja, Nexus, ich habe mir schon ein ParameterType-Template geschrieben, dass entsprechend Referenzen oder Per-Value macht, je nachdem wie groß die Typen sind, das könnte man hier natürlich einsetzen, aber hätte nicht der übersichtlichen Darstellung der Idee gedient^^
-
Dravere, ich kann das alles nachvollziehen, was du schreibst. Allerdings ist meinem Beispiel nicht der Vorteil tragend, dass man sich das () spart, sondern dass man die intuitive Zuweisungs-Operation benutzen kann (zumindest in meinen Augen). Zudem hat man intern immernoch den setter und den getter, sodass man dort alles machen kann, was man auch normalerweise tun kann. Mit den Kosten hast du natürlich recht, mir ist auch ein Dorn im Auge, dass ich den this-Zeiger mitspeichern muss, aber die Kosten sind nicht so hoch wie tr1::function und ähnlich dynamisches (das wollte ich ja gezielt vermeiden, wie ich auch schon schrieb).
Ich hasse solche Konflikte, ganz ehrlich