Methoden mit bool-Parametern und "Lesbarkeit" im Kontext
-
.filmor schrieb:
Xin schrieb:
[...]Man könnte genauso gut #defines nehmen.
Das stimmt so nicht.
enum A { a, b }; enum B { x, y }; void foo (A x) {} int main () { foo (0); foo (x); }liefert folgende wunderschöne Compilerfehler:
test.cpp: In function 'int main()': test.cpp:9: error: invalid conversion from 'int' to 'A' test.cpp:9: error: initializing argument 1 of 'void foo(A)' test.cpp:10: error: cannot convert 'B' to 'A' for argument '1' to 'void foo(A)'Das enum's nicht besonders toll sind will ich nicht bestreiten, aber so mies muss man sie auch nicht machen.
Wow, das muss ich morgen nochmal ausprobieren. Als ich das letzte Mal enum nutzte, wurden die einfach als int verbraten. Darum habe ich sie eigentlich seitdem ignoriert.
-
Xin schrieb:
Ich programmiere C++.

dachte ich haette ein C++ Beispiel gebracht?

-
@Xin: Die Verwirrung um verschiedene USE_BORDER's kann dir mit deiner Flag-Klasse genauso passieren wie mit Bit-Arithmetik

@Shade:
Shade Of Mine schrieb:
DEvent schrieb:
Btw, wieso nicht einfach ein enum nehmen? Die sind doch auch typsicher, oder?
Nicht wirklich:
enum T { bla, blup }; void foo(int mode) { } foo(7);Ja, wenn die Funktion einen int erwartet, kann sie natürlich einen beliebigen int-Wert bekommen. Wenn du die Definition änderst auf
void foo(T mode), wird sich dein C++ Compiler zu recht beschweren, daß er eine explizite Typumwandlung benötigt (wenn es um C geht, hast du allerdings recht).
-
CStoll schrieb:
Ja, wenn die Funktion einen int erwartet, kann sie natürlich einen beliebigen int-Wert bekommen. Wenn du die Definition änderst auf
void foo(T mode), wird sich dein C++ Compiler zu recht beschweren, daß er eine explizite Typumwandlung benötigt (wenn es um C geht, hast du allerdings recht).enum T { bla, blup }; void foo(T mode) { } foo(bla | blup); //errorman ist auf int festgelegt wenn man enums sinnvoll verwenden will. in ganz wenig ausnahmefällen kann man ein foo(T mode) machen, aber in den meisten Fällen braucht man int - denn wozu flags wenn ich sie nicht kombinieren kann?
-
Also eine Lösung, die das Problem löst, wird wohl schwieriger sein. Entweder du hast eine implizite Umwandlung von int nach Flag (wie bei C enum's), dann kann der Compiler aber nicht sicherstellen, daß die übergebenen int's legal sind - oder du hast keine solche Umwandlung (wie bei C++ enum's), dann funktionieren diese Flag-Kombinationen üblicherweise nicht. Am besten ist da immer noch die Flag-Klasse von Xin, allerdings als eigenständige Klasse und nicht als Basis.
*grübelt* enum's sind doch eigenständige Typen - also wäre es auch erlaubt, die Bit-Operatoren für einen eigenen enum zu überladen:
enum Flag {...}; Flag operator|(Flag r, Flag l) { return (Flag)((int)r|(int)l); } ...
-
Jetzt noch den einen Slash durch ein Klammer auf ersetzt und es würde tatsächlich gehen, ja.
-
CStoll schrieb:
@Xin: Die Verwirrung um verschiedene USE_BORDER's kann dir mit deiner Flag-Klasse genauso passieren wie mit Bit-Arithmetik

C++ kann die Typen WindowFlags und ButtonFlags problemlos unterscheiden.
Und da WindowFlags keinen Operator & anbietet, der auf ButtonFlags reagiert, wird der Compiler meckern.CStoll schrieb:
Am besten ist da immer noch die Flag-Klasse von Xin, allerdings als eigenständige Klasse und nicht als Basis.
Was spricht dagegen die Klasse als Basis zu nutzen. Klappt hier wunderbar.
Die Klasse benötige ich nur, um WindowFlags von Windows zu unterscheiden und WindowFlags von integers, wenn ich Operatoren überlade.
-
Wozu hast du eigentlich
MyClass : MyClassFlags { }, Xin?
-
wie wärs mit
window open title: "title" fullscreen: true.
ach moment das is ja die verbotene sprache

-
DEvent schrieb:
Wozu hast du eigentlich
MyClass : MyClassFlags { }, Xin?
Wenn ich die Frage richtig verstehe...?
Ich mache die Ableitung, weil mein Objekt Flags beinhaltet. Statt eine "hat ein" Beziehung zu machen und eine public-Variable Flags einzuführen, mache ich eine "ist ein" Beziehung daraus, um mir Schreibarbeit zu sparen.
Die Sache ist ungefährlich, weil es sonst nichts gibt, was sich für MyClassFlags interessiert und so muss ich nicht immer betonen, dass ich mit den Flags des Objektes arbeite.Ich will ja wissen, ob das Fenster Fullscreen ist ( "ist ein" Fullscreen-Fenster)
if( MyWindow & WinFlgs::Fullscreen ) ...und nicht ob die Flags Fullscreen signalisieren. ("hat ein" Fullscreen-Fenster zu sein)
if( MyWindow->Flags & WinFlgs::Fullscreen ) ...Ich finde das semantisch logischer und ich muss halt immernoch weniger tippen

-
Und man verwirrt den Anwender des Codes ungemein. Vorallem wenn man nachher noch eine neue Flagvariante einführt, die zB das backbuffering oder so betrifft
dann wird alles sehr sehr chaotisch.schreibarbeit darf _nie_ ein grund sein etwas zu machen.
-
Xin schrieb:
DEvent schrieb:
Wozu hast du eigentlich
MyClass : MyClassFlags { }, Xin?
Wenn ich die Frage richtig verstehe...?
Ich mache die Ableitung, weil mein Objekt Flags beinhaltet. Statt eine "hat ein" Beziehung zu machen und eine public-Variable Flags einzuführen, mache ich eine "ist ein" Beziehung daraus, um mir Schreibarbeit zu sparen.
Die Sache ist ungefährlich, weil es sonst nichts gibt, was sich für MyClassFlags interessiert und so muss ich nicht immer betonen, dass ich mit den Flags des Objektes arbeite.Ich will ja wissen, ob das Fenster Fullscreen ist ( "ist ein" Fullscreen-Fenster)
if( MyWindow & WinFlgs::Fullscreen ) ...und nicht ob die Flags Fullscreen signalisieren. ("hat ein" Fullscreen-Fenster zu sein)
if( MyWindow->Flags & WinFlgs::Fullscreen ) ...Ich finde das semantisch logischer und ich muss halt immernoch weniger tippen

Sowas funktioniert aber nur mit öffentlicher Vererbung (aber die ist für die Konstallation KlassenFlag - Klasse wenig sinnvoll) oder im Inneren deiner Klassenmethoden (aber dann gibt es keinen Aufwand-Unterschied zwischen 'Flags&WinFlgs::Fullscreen' und '*this&WinFlgs::Fullscreen'). Und du solltest mal darüber nachdenken, was "ist-ein" bzw. "hat-ein" im OOP-Kontext eigentlich bedeutet (Tip: Die Beziehungen beziehen sich auf die beteiligten Klassen).
-
CStoll schrieb:
Xin schrieb:
DEvent schrieb:
Wozu hast du eigentlich
MyClass : MyClassFlags { }, Xin?
Wenn ich die Frage richtig verstehe...?
Ich mache die Ableitung, weil mein Objekt Flags beinhaltet. Statt eine "hat ein" Beziehung zu machen und eine public-Variable Flags einzuführen, mache ich eine "ist ein" Beziehung daraus, um mir Schreibarbeit zu sparen.
Die Sache ist ungefährlich, weil es sonst nichts gibt, was sich für MyClassFlags interessiert und so muss ich nicht immer betonen, dass ich mit den Flags des Objektes arbeite.Ich will ja wissen, ob das Fenster Fullscreen ist ( "ist ein" Fullscreen-Fenster)
if( MyWindow & WinFlgs::Fullscreen ) ...und nicht ob die Flags Fullscreen signalisieren. ("hat ein" Fullscreen-Fenster zu sein)
if( MyWindow->Flags & WinFlgs::Fullscreen ) ...Ich finde das semantisch logischer und ich muss halt immernoch weniger tippen

Sowas funktioniert aber nur mit öffentlicher Vererbung
Von meiner Seite wurde nie etwas anderes behauptet:
Xin schrieb:
class MyClass : public MyClassFlags {};Warum richtest Du diesen Comment dann an mich?
CStoll schrieb:
(aber die ist für die Konstallation KlassenFlag - Klasse wenig sinnvoll)
Das halte ich sogar für sehr sinnvoll. Von Entwickler wird natürlich hier ein Perspektivenwechsel verlangt. Und genau das ist doch, was den klassenorientierten Ansatz vom strukturierten unterscheidet. Das sollte einem C++-Entwickler also nicht nur zuzumuten sein, sondern er könnte den Aufbau in Hirachien sogar besonders zu schätzen wissen.
Ein Window ist ein WindowFlags. Die Flags beschreiben nicht das Fenster, sondern das Fenster ist eine Interpretation der Flags. Die WindowFlags werden erweitert, so dass ein viereckiger Kasten (genannt Window) entsteht, der sich an den Flags orientiert.
CStoll schrieb:
oder im Inneren deiner Klassenmethoden (aber dann gibt es keinen Aufwand-Unterschied zwischen 'Flags&WinFlgs::Fullscreen' und '*this&WinFlgs::Fullscreen'). Und du solltest mal darüber nachdenken, was "ist-ein" bzw. "hat-ein" im OOP-Kontext eigentlich bedeutet (Tip: Die Beziehungen beziehen sich auf die beteiligten Klassen).
Darum heißt es ja auch Objektorientiert, weil es sich auf die Klassen bezieht... jaja, ich habe schon viel in diesem Forum gelernt.
Auch hier biete ich eine Möglichkeit an. Darum heißt es nicht "solltest Du mal darüber nachdenken", sondern "Du hast darüber nachgedacht und das Gesagte ist dabei rausgekommen".
lolz erfragte Möglichkeiten und wenn Dir funktionierende Antworten darauf nicht gefallen, darfst Du gerne mal darüber nachdenken, sie nicht zu benutzen. Ich freue mich auch, wenn Du Deine qualifizierte Kritik kundtust. Ob Du die mit einem "darüber solltest Du mal nachdenken" mitteilen solltest, halte ich in diesem Fall für zweifelhaft, weil der Fall exakt das beschreibt, was er Deiner Kritik nach nicht beschreibt. Die Bedeutung der Hirachie muss man natürlich sehen und wenn sie Dir dann nicht gefällt, ist das vollkommen in Ordnung. Das wäre eine Geschmacksfrage, keine Frage fachlicher Kompetenz, über die man "nachdenken sollte".
Daher sage ich Dir, dass Du einen freundlichen gemeinten Rat nicht nur geben kannst, Du darfst ihn auch selbst beherzigen.
Und Deinen freundlichen Seitenhieb mit dem "Tip" darfst Du Dir entsprechend schenken.Deine Kritik erfordert mehr Operatoren im laufenden Sourcetext, die zwar der Compiler so oder so ausführen muss, aber der Entwickler überall im Sourcecode schreiben und - viel schlimmer - immer wieder lesen muss. Das macht den Sourcecode unnötig schwieriger zu lesen. Das scheitert sicherlich nicht an einer Klasse, aber solche Mängel kann man ja an beliebig vielen Klassen begehen, was das Verständnis eines eigentlich trivialen, selbstdokumentierenden Sourcecodes schnell in den Bereich "hebt", der vom Leser nicht mehr als trivial verstanden wird.
Viele Entwickler überarbeiten nicht triviale Sourcecodes mal eben schnell und bauen so Fehler ein, die man dann wieder in nicht trivialen Sourcecodes suchen muss.Das bringt mich wieder zum gleichen Ergebnis wie oben: "Das halte ich sogar für sehr sinnvoll."
Und ich weiß sogar warum: Ich habe darüber so gründlich nachgedacht, weil ich diesen Mechanismus in meiner Programmiersprache automatisieren möchte.
-
Xin schrieb:
CStoll schrieb:
(aber die ist für die Konstallation KlassenFlag - Klasse wenig sinnvoll)
Das halte ich sogar für sehr sinnvoll. Von Entwickler wird natürlich hier ein Perspektivenwechsel verlangt. Und genau das ist doch, was den klassenorientierten Ansatz vom strukturierten unterscheidet. Das sollte einem C++-Entwickler also nicht nur zuzumuten sein, sondern er könnte den Aufbau in Hirachien sogar besonders zu schätzen wissen.
Ein Window ist ein WindowFlags. Die Flags beschreiben nicht das Fenster, sondern das Fenster ist eine Interpretation der Flags. Die WindowFlags werden erweitert, so dass ein viereckiger Kasten (genannt Window) entsteht, der sich an den Flags orientiert.
Ich sehe schon, da prallen zwei Perspektiven aufeinander - imho ist ein Fenster mehr als eine "Erweiterung" einiger Flags, ein Fenster hat Flags, die seine Darstellung beschreiben. Oder würdest du ein Auto auch von Geschwindigkeit, Lenkrichtung etc ableiten, weil das Auto diese Werte interpretiert? Und nebenbei stehen verschiedene Fenster noch in einer eigenen Vererbungshierarchie - wenn da jede Klasse ihre eigenen Flags hat, ist das Chaos vorprogrammiert.
CStoll schrieb:
oder im Inneren deiner Klassenmethoden (aber dann gibt es keinen Aufwand-Unterschied zwischen 'Flags&WinFlgs::Fullscreen' und '*this&WinFlgs::Fullscreen'). Und du solltest mal darüber nachdenken, was "ist-ein" bzw. "hat-ein" im OOP-Kontext eigentlich bedeutet (Tip: Die Beziehungen beziehen sich auf die beteiligten Klassen).
Darum heißt es ja auch Objektorientiert, weil es sich auf die Klassen bezieht... jaja, ich habe schon viel in diesem Forum gelernt.
Kennst du das klassische Beispiel der Fehlinterpretiation von "ist-ein"? Jeder Mathematiker wird dir bestätigen, daß ein Quadrat ein Rechteck "ist" - aber aus OOP-Sicht ist es trotzdem falsch,
class quadrat : public rechteck{...}zu schreiben.Und macht es in deinen Augen Sinn, Fenster miteinander zu verunden oder zu verodern? Wenn ja, was soll herauskommen, wenn ich "fenster1&fenster2" schreibe?
Auch hier biete ich eine Möglichkeit an. Darum heißt es nicht "solltest Du mal darüber nachdenken", sondern "Du hast darüber nachgedacht und das Gesagte ist dabei rausgekommen".
lolz erfragte Möglichkeiten und wenn Dir funktionierende Antworten darauf nicht gefallen, darfst Du gerne mal darüber nachdenken, sie nicht zu benutzen. Ich freue mich auch, wenn Du Deine qualifizierte Kritik kundtust. Ob Du die mit einem "darüber solltest Du mal nachdenken" mitteilen solltest, halte ich in diesem Fall für zweifelhaft, weil der Fall exakt das beschreibt, was er Deiner Kritik nach nicht beschreibt. Die Bedeutung der Hirachie muss man natürlich sehen und wenn sie Dir dann nicht gefällt, ist das vollkommen in Ordnung. Das wäre eine Geschmacksfrage, keine Frage fachlicher Kompetenz, über die man "nachdenken sollte".OK, du hast eine Lösung geliefert, die allerdings aus OOP-Sicht (entschuldige den Ausdruck) vollkommener Käse ist. Und den Vorteil gegenüber einem (normalen oder selbstdefinierten) Enum, der die etwas exotische Syntax rechtfertigen könnte, sehe ich auch noch nicht.
Deine Kritik erfordert mehr Operatoren im laufenden Sourcetext, die zwar der Compiler so oder so ausführen muss, aber der Entwickler überall im Sourcecode schreiben und - viel schlimmer - immer wieder lesen muss. Das macht den Sourcecode unnötig schwieriger zu lesen. Das scheitert sicherlich nicht an einer Klasse, aber solche Mängel kann man ja an beliebig vielen Klassen begehen, was das Verständnis eines eigentlich trivialen, selbstdokumentierenden Sourcecodes schnell in den Bereich "hebt", der vom Leser nicht mehr als trivial verstanden wird.
Viele Entwickler überarbeiten nicht triviale Sourcecodes mal eben schnell und bauen so Fehler ein, die man dann wieder in nicht trivialen Sourcecodes suchen muss.Und du meinst, mit deiner Lösung erhöhst du die Lesbarkeit eines Codes? Wenn es dir um den Schreibaufwand bei der Verwendung der Klasse geht, ist es vermutlich besser, für jedes Flag eine eigene Getter/Setter-Methode (wnd.IsFullscreen() bzw. wnd.SetFullscreen(bool)) zu definieren - oder wenn dir das zu aufwendig ist, auch eine Universal-Get/Set-Methode (IsSet(Flags) bzw. SetFlags(Flags), die mit enum's gefüttert werden).
Das bringt mich wieder zum gleichen Ergebnis wie oben: "Das halte ich sogar für sehr sinnvoll."
Und ich weiß sogar warum: Ich habe darüber so gründlich nachgedacht, weil ich diesen Mechanismus in meiner Programmiersprache automatisieren möchte.Dann sieh mal zu, was herauskommt, wenn du deine WindowFlags/Window-Hierarchie in größeren Projekten einsetzt:
class WindowFlags//allgemeine Flags { ... }; class Window : public WindowFlags//Fenster-Basisklasse { ... }; class DialogFlags//Flags für Dialogfenster { ... }; class Dialog : public Window, public DialogFlags { ... };Damit verdoppelst du die vorhandene Hierarchie und verknotest dich irgendwann in Mehrdeutigkeiten und potentiellen Fehlerquellen.
-
CStoll schrieb:
Ich sehe schon, da prallen zwei Perspektiven aufeinander - imho ist ein Fenster mehr als eine "Erweiterung" einiger Flags, ein Fenster hat Flags, die seine Darstellung beschreiben. Oder würdest du ein Auto auch von Geschwindigkeit, Lenkrichtung etc ableiten, weil das Auto diese Werte interpretiert? Und nebenbei stehen verschiedene Fenster noch in einer eigenen Vererbungshierarchie - wenn da jede Klasse ihre eigenen Flags hat, ist das Chaos vorprogrammiert.
Jow, das mit den unterschiedlichen Perspektiven passiert mir in dem Forum in letzter Zeit häufiger ;->
Geschwindkeit oder die Richtung, in die es lenkt, sind keine Flags.
Es gibt kein Chaos, da Window sich genauso verhält, als wäre es mit Window::Flags implementiert worden.
CStoll schrieb:
Kennst du das klassische Beispiel der Fehlinterpretiation von "ist-ein"? Jeder Mathematiker wird dir bestätigen, daß ein Quadrat ein Rechteck "ist" - aber aus OOP-Sicht ist es trotzdem falsch,
class quadrat : public rechteck{...}zu schreiben.Yepp, kenne ich und trifft hier nicht zu, also verstehe ich nicht, was Du hiermit aussagen möchtest.
Windows enthalten WindowFlags und beide können im Bedarfsfall auf die Flags reduziert werden - ob das jetzt per MyWindow->Flags passiert, oder durch eine Typkonvertierung auf WindowFlags macht da keinen Unterschied.
CStoll schrieb:
OK, du hast eine Lösung geliefert, die allerdings aus OOP-Sicht (entschuldige den Ausdruck) vollkommener Käse ist. Und den Vorteil gegenüber einem (normalen oder selbstdefinierten) Enum, der die etwas exotische Syntax rechtfertigen könnte, sehe ich auch noch nicht.
Ob das Käse ist, wie Du allgemeingültig behauptest, hast Du mir jetzt nicht schlüssig begründet. Das "sehe ich noch nicht", sehe ich allerdings positiv, denn es kennzeichnet eine Meinungsäußerung.
Zum Rechtfertigen komme ich gleich noch...CStoll schrieb:
Deine Kritik erfordert mehr Operatoren im laufenden Sourcetext, die zwar der Compiler so oder so ausführen muss, aber der Entwickler überall im Sourcecode schreiben und - viel schlimmer - immer wieder lesen muss. Das macht den Sourcecode unnötig schwieriger zu lesen. Das scheitert sicherlich nicht an einer Klasse, aber solche Mängel kann man ja an beliebig vielen Klassen begehen, was das Verständnis eines eigentlich trivialen, selbstdokumentierenden Sourcecodes schnell in den Bereich "hebt", der vom Leser nicht mehr als trivial verstanden wird.
Viele Entwickler überarbeiten nicht triviale Sourcecodes mal eben schnell und bauen so Fehler ein, die man dann wieder in nicht trivialen Sourcecodes suchen muss.Und du meinst, mit deiner Lösung erhöhst du die Lesbarkeit eines Codes?
Ja, meine ich. Und ich meine, die Fehleranfälligkeit von Codes zu reduzieren.
Aber das ist vielleicht eine subjektive Frage, schau Dir die Codes an und entscheide für Dich.CStoll schrieb:
Das bringt mich wieder zum gleichen Ergebnis wie oben: "Das halte ich sogar für sehr sinnvoll."
Und ich weiß sogar warum: Ich habe darüber so gründlich nachgedacht, weil ich diesen Mechanismus in meiner Programmiersprache automatisieren möchte.Dann sieh mal zu, was herauskommt, wenn du deine WindowFlags/Window-Hierarchie in größeren Projekten einsetzt: [...]
Damit verdoppelst du die vorhandene Hierarchie und verknotest dich irgendwann in Mehrdeutigkeiten und potentiellen Fehlerquellen.
In Mehrdeutigkeiten verheddert man sich eben nicht, weil FensterFlags und DialogFlags unterschiedliche Typen sind, die eindeutig aufgelöst werden können. Das ist der Grund, der diese aufwendige C++Syntax sich grade in größeren Projekten rechtfertigt.
Potentielle Fehlerquellen? Ist das jetzt einfach nur mal dahergeschrieben oder steckt da eine belegbare Befürchtung hinter?Bleiben wir bei Deinem Beispiel und überlegen uns wie das ganze aussieht, wenn man die Unterscheidung in unterschiedliche Klassen weglässt:
class Window { public: flagtype Flags; ... }; class Dialog : public Window { public: flagtype Flags; ... };Mit Enum lautet der Zugriff nun
Dialog d; Window w; if( w->Flags & USE_FULLSCREEN ) ... // funktioniert :-) if( d->Flags & USE_FULLSCREEN ) ... // funktioniert :-) macht aber nicht, was gewünscht ist: Kabooom! :-( if( d->Window::Flags & USE_FULLSCREEN ) ... // funktioniert - hoffentlich vergisst der Entwickler das Window:: nie... if( d->Flags & MODAL ) ... // funktioniert :-)Alternativ benötigt man eine Form von ungarischer Notation und zwar grundsätzlich, auch Window darf keine Flags mehr haben, sondern muss WindowFlags haben, damit die Verlockung allgemein auf "Flags" zuzugreifen nicht gegeben ist.
Dialog d; Window w; if( w->WindowFlags & USE_FULLSCREEN ) ... // funktioniert :-) if( d->WindowFlags & USE_FULLSCREEN ) ... // hoffentlich vertippt man sich im Eifer des Gefechts nicht und schreibt d->Dialogflags, schließlich bearbeitet man eine Dialog if( d->DialogFlags & USE_FULLSCREEN ) ... // whoops, vertan, kompiliert aber :-) und knallt zur Laufzeit :-( if( d->DialogFlags & MODAL ) ...Jetzt kann man noch jede Menge Getter und Setter schreiben, aber warum etliche Funktionen schreiben, um eine Klasse einzusparen, die größtenteils per Template erzeugt werden kann?
Meine Angebot sieht so aus:
Window w; Dialog d; if( w & WindowFlags::UseFullScreen ) ... // funktioniert :-) if( d & WindowFlags::UseFullScreen ) ... // funktioniert :-) if( d & DialogFlags::UseFullScreen ) ... // Whoops, vertan, Compiler meckert. if( d & DialogFlags::Modal ) ... // funktioniert :-)Keine Ahnung, wo Du potentielle Gefahren siehst, aber wenn Du sie siehst, lass sie mich wissen.
-
Xin schrieb:
CStoll schrieb:
Ich sehe schon, da prallen zwei Perspektiven aufeinander - imho ist ein Fenster mehr als eine "Erweiterung" einiger Flags, ein Fenster hat Flags, die seine Darstellung beschreiben. Oder würdest du ein Auto auch von Geschwindigkeit, Lenkrichtung etc ableiten, weil das Auto diese Werte interpretiert? Und nebenbei stehen verschiedene Fenster noch in einer eigenen Vererbungshierarchie - wenn da jede Klasse ihre eigenen Flags hat, ist das Chaos vorprogrammiert.
Jow, das mit den unterschiedlichen Perspektiven passiert mir in dem Forum in letzter Zeit häufiger ;->
Geschwindkeit oder die Richtung, in die es lenkt, sind keine Flags.
Die Geschwindigkeit bestimmt genauso das Verhalten eines Autos wie die Flags das Verhalten eines Fensters kontrollieren (aber wenn dir Geschwindigkeit nicht gefällt, nimm die Wagenfarbe).
Es gibt kein Chaos, da Window sich genauso verhält, als wäre es mit Window::Flags implementiert worden.
Du hast mir immer noch nicht verraten, was der Ausdruck 'wnd1&wnd2' für eine Bedeutung haben soll.
CStoll schrieb:
Kennst du das klassische Beispiel der Fehlinterpretiation von "ist-ein"? Jeder Mathematiker wird dir bestätigen, daß ein Quadrat ein Rechteck "ist" - aber aus OOP-Sicht ist es trotzdem falsch,
class quadrat : public rechteck{...}zu schreiben.Yepp, kenne ich und trifft hier nicht zu, also verstehe ich nicht, was Du hiermit aussagen möchtest.
Windows enthalten WindowFlags und beide können im Bedarfsfall auf die Flags reduziert werden - ob das jetzt per MyWindow->Flags passiert, oder durch eine Typkonvertierung auf WindowFlags macht da keinen Unterschied.
Du sagst "enthalten" - und das steht ehr für eine "hat-ein"-Beziehung. (btw, ein Fenster enthält auch seine Position, den Text der Titelleiste etc. - soll es dafür auch von Rect, String etc abgeleitet werden?)
CStoll schrieb:
OK, du hast eine Lösung geliefert, die allerdings aus OOP-Sicht (entschuldige den Ausdruck) vollkommener Käse ist. Und den Vorteil gegenüber einem (normalen oder selbstdefinierten) Enum, der die etwas exotische Syntax rechtfertigen könnte, sehe ich auch noch nicht.
Ob das Käse ist, wie Du allgemeingültig behauptest, hast Du mir jetzt nicht schlüssig begründet. Das "sehe ich noch nicht", sehe ich allerdings positiv, denn es kennzeichnet eine Meinungsäußerung.
Zum Rechtfertigen komme ich gleich noch...Ich kann ja nichts dafür, wenn du meine Begründung nicht akzeptiert. Ein Fenster HAT Flags - und ist kein (aufgewertetes) Flag.
CStoll schrieb:
Deine Kritik erfordert mehr Operatoren im laufenden Sourcetext, die zwar der Compiler so oder so ausführen muss, aber der Entwickler überall im Sourcecode schreiben und - viel schlimmer - immer wieder lesen muss. Das macht den Sourcecode unnötig schwieriger zu lesen. Das scheitert sicherlich nicht an einer Klasse, aber solche Mängel kann man ja an beliebig vielen Klassen begehen, was das Verständnis eines eigentlich trivialen, selbstdokumentierenden Sourcecodes schnell in den Bereich "hebt", der vom Leser nicht mehr als trivial verstanden wird.
Viele Entwickler überarbeiten nicht triviale Sourcecodes mal eben schnell und bauen so Fehler ein, die man dann wieder in nicht trivialen Sourcecodes suchen muss.Und du meinst, mit deiner Lösung erhöhst du die Lesbarkeit eines Codes?
Ja, meine ich. Und ich meine, die Fehleranfälligkeit von Codes zu reduzieren.
Aber das ist vielleicht eine subjektive Frage, schau Dir die Codes an und entscheide für Dich.Das habe ich bereits (für mich) entschieden

CStoll schrieb:
Das bringt mich wieder zum gleichen Ergebnis wie oben: "Das halte ich sogar für sehr sinnvoll."
Und ich weiß sogar warum: Ich habe darüber so gründlich nachgedacht, weil ich diesen Mechanismus in meiner Programmiersprache automatisieren möchte.Dann sieh mal zu, was herauskommt, wenn du deine WindowFlags/Window-Hierarchie in größeren Projekten einsetzt: [...]
Damit verdoppelst du die vorhandene Hierarchie und verknotest dich irgendwann in Mehrdeutigkeiten und potentiellen Fehlerquellen.
In Mehrdeutigkeiten verheddert man sich eben nicht, weil FensterFlags und DialogFlags unterschiedliche Typen sind, die eindeutig aufgelöst werden können. Das ist der Grund, der diese aufwendige C++Syntax sich grade in größeren Projekten rechtfertigt.
Wenn du das sagst - ich trau dem Frieden noch nicht.
Potentielle Fehlerquellen? Ist das jetzt einfach nur mal dahergeschrieben oder steckt da eine belegbare Befürchtung hinter?
Du mußt alle deine Flag-Klassen identisch aufbauen - wenn du da eine Kleinigkeit vergisst, klappt es plötzlich nicht mehr so wie gewünscht. Und zeig mir mal, wie man die Flag-Klassen per Template erzeugen kann.
Bleiben wir bei Deinem Beispiel und überlegen uns wie das ganze aussieht, wenn man die Unterscheidung in unterschiedliche Klassen weglässt:
Meine Angebot sieht so aus:
Window w; Dialog d; if( w & WindowFlags::UseFullScreen ) ... // funktioniert :-) if( d & WindowFlags::UseFullScreen ) ... // funktioniert :-) if( d & DialogFlags::UseFullScreen ) ... // Whoops, vertan, Compiler meckert. if( d & DialogFlags::Modal ) ... // funktioniert :-)Keine Ahnung, wo Du potentielle Gefahren siehst, aber wenn Du sie siehst, lass sie mich wissen.
Siehe oben - schonmal versucht, was bei 'if(w&d)' herauskommt? Sowas ist weder besonders lesbar noch macht es irgendetwas sinnvolles - aber es wird anstandslos compiliert. Da ist es mir lieber, genau zu wissen, wann ich denn nun auf die Flags zugreife (Sprich: Im Endeffekt kann ich mich mit der Flag-Klasse schon anfreunden, aber die Vererbungsbeziehung halte ich immer noch für sinnlos).
-
CStoll schrieb:
Es gibt kein Chaos, da Window sich genauso verhält, als wäre es mit Window::Flags implementiert worden.
Du hast mir immer noch nicht verraten, was der Ausdruck 'wnd1&wnd2' für eine Bedeutung haben soll.
Wenn operator & für zwei Fenster nicht deklariert ist, würde eine Hirachie hochgegangen und das Ergebnis wären übereinstimmende WindowFlags, genauso wie wnd1->Flags & wnd2->Flags.
Wenn Dir das nicht gefällt, deklariere operator & als private member.Ich sehe nicht, wo sich hier ein Problem auftuen könnte.
CStoll schrieb:
Kennst du das klassische Beispiel der Fehlinterpretiation von "ist-ein"? Jeder Mathematiker wird dir bestätigen, daß ein Quadrat ein Rechteck "ist" - aber aus OOP-Sicht ist es trotzdem falsch,
class quadrat : public rechteck{...}zu schreiben.Yepp, kenne ich und trifft hier nicht zu, also verstehe ich nicht, was Du hiermit aussagen möchtest.
Windows enthalten WindowFlags und beide können im Bedarfsfall auf die Flags reduziert werden - ob das jetzt per MyWindow->Flags passiert, oder durch eine Typkonvertierung auf WindowFlags macht da keinen Unterschied.
Du sagst "enthalten" - und das steht ehr für eine "hat-ein"-Beziehung. (btw, ein Fenster enthält auch seine Position, den Text der Titelleiste etc. - soll es dafür auch von Rect, String etc abgeleitet werden?)[/quote]
Das ist genau das, was man sich von wiederverwendbaren Klassen wünscht.In meinem Klassen gibt's keine Fenster, ich schreibe ja kein OS. Aber was die Titelleiste angeht, finde ich viele Parallelen bei meinen Klassen.
Icons sind bei mir abgeleitet von u.a. BitMaps (die von Flächen abgeleitet sind) und Strings und Listenelementen und Punkten (Position), weil Icons haben Namen und weil Icons von Strings abgeleitet ist, darf Liste auch nach Namen suchen usw...Wo ist das Problem? Ich sehe hier bisher vorrangig Vorteile.
CStoll schrieb:
Ich kann ja nichts dafür, wenn du meine Begründung nicht akzeptiert. Ein Fenster HAT Flags - und ist kein (aufgewertetes) Flag.
Man kann sich vieles von verschiedenen Perspektiven ansehen. Ich würde mich an Deiner Stelle nicht so allgemeingültig festlegen.
CStoll schrieb:
Potentielle Fehlerquellen? Ist das jetzt einfach nur mal dahergeschrieben oder steckt da eine belegbare Befürchtung hinter?
Du mußt alle deine Flag-Klassen identisch aufbauen - wenn du da eine Kleinigkeit vergisst, klappt es plötzlich nicht mehr so wie gewünscht. Und zeig mir mal, wie man die Flag-Klassen per Template erzeugen kann.
Warum müsste ich die Flag-Klassen identisch aufbauen!?
Was braucht man denn für eine Flag-Klasse? Die typbezogenen Operatoren und die Namen den Wert der Flags.
class WindowFlag : public FlagOperator< WindowFlag > { private: WindowFlag( int value ) : FlagOperator< WindowFlag >( value ) {} public: static const WindowFlag UseFullScreen, UseBorder; }; .cpp: const WindowFlag WindowFlag::UseFullScreen( 1 ); const WindowFlag WindowFlag::UseFullScreen( 2 );So what?
CStoll schrieb:
Bleiben wir bei Deinem Beispiel und überlegen uns wie das ganze aussieht, wenn man die Unterscheidung in unterschiedliche Klassen weglässt:
Meine Angebot sieht so aus:
Window w; Dialog d; if( w & WindowFlags::UseFullScreen ) ... // funktioniert :-) if( d & WindowFlags::UseFullScreen ) ... // funktioniert :-) if( d & DialogFlags::UseFullScreen ) ... // Whoops, vertan, Compiler meckert. if( d & DialogFlags::Modal ) ... // funktioniert :-)Keine Ahnung, wo Du potentielle Gefahren siehst, aber wenn Du sie siehst, lass sie mich wissen.
Siehe oben - schonmal versucht, was bei 'if(w&d)' herauskommt? Sowas ist weder besonders lesbar noch macht es irgendetwas sinnvolles - aber es wird anstandslos compiliert.
Deklariere es als private und es kompiliert nicht mehr. Es ist ja nicht so, als ob diese Sachen nicht möglich wären.
Hier stelle ich aber auch mal die Frage, ob das w&d wirklich interessant ist. Das hat genausowenig Sinn wie ( w->WindowFlags & d->WindowFlags ). Wozu soll das gut sein? Welche Frage soll das beantworten? Wenn beide Objekte mindestens eine beliebige Gemeinsamkeiten haben, dann...?
Versuchen wir etwas Sinn in die Fragestellung zu bringen.
if( WindowFlag::UseFullscreen & window & dialog ) ...ist genauso fragwürdig wie
if( USE_FULLSCREEN & window->WindowFlags & dialog->WindowFlags ) ...Derartige Abfragen kommen in meiner Programmierung äußerst selten vor und dann lässt sich das notfalls auch lesbar darstellen, indem man in diesem eher seltenen Fall dem Leser klarstellt, dass man auf WindowFlags rauswill.
if( static_cast< WindowFlags & >( window ) & static_cast< WindowFlags & >( dialog ) ) ...Im alltäglichen Fall - und der ist mir im Alltag wichtiger - ist das nicht erforderlich.
CStoll schrieb:
Da ist es mir lieber, genau zu wissen, wann ich denn nun auf die Flags zugreife (Sprich: Im Endeffekt kann ich mich mit der Flag-Klasse schon anfreunden, aber die Vererbungsbeziehung halte ich immer noch für sinnlos).
Damit kann ich gut leben. Hier stellst Du klar, dass es Dir nicht gefällt und das klingt anders als "Du solltest mal darüber nachdenken". Es ist eine Möglichkeit und es gibt viele Möglichkeiten in C++ Daten zu verarbeiten. Wenn Du Dich mit der Flag-Klasse anfreunden kannst, umso besser. Vielleicht freundest Du Dich mit der Zeit auch an die andere Perspektive an.
-
Xin schrieb:
Das halte ich sogar für sehr sinnvoll. Von Entwickler wird natürlich hier ein Perspektivenwechsel verlangt. Und genau das ist doch, was den klassenorientierten Ansatz vom strukturierten unterscheidet. Das sollte einem C++-Entwickler also nicht nur zuzumuten sein, sondern er könnte den Aufbau in Hirachien sogar besonders zu schätzen wissen.
Ein Window ist ein WindowFlags. Die Flags beschreiben nicht das Fenster, sondern das Fenster ist eine Interpretation der Flags. Die WindowFlags werden erweitert, so dass ein viereckiger Kasten (genannt Window) entsteht, der sich an den Flags orientiert.
Jetzt versteh ich das ueberhaupt. Das ist wirklich ein sehr interessanter Ansatz und wird doch nur wegen der Mehrfachvererbung in C++ moeglich.
Ich weis nur nich obs Sinn macht eine "ist eine" Beziehung von einem Dialog und seinen Flags zu haben.
Xin schrieb:
Wenn operator & für zwei Fenster nicht deklariert ist, würde eine Hirachie hochgegangen und das Ergebnis wären übereinstimmende WindowFlags, genauso wie wnd1->Flags & wnd2->Flags.
Wenn Dir das nicht gefällt, deklariere operator & als private member.Was passiert wenn du von meheren Flag-Klassen ableistest?
-
DEvent schrieb:
Xin schrieb:
Das halte ich sogar für sehr sinnvoll. Von Entwickler wird natürlich hier ein Perspektivenwechsel verlangt. Und genau das ist doch, was den klassenorientierten Ansatz vom strukturierten unterscheidet. Das sollte einem C++-Entwickler also nicht nur zuzumuten sein, sondern er könnte den Aufbau in Hirachien sogar besonders zu schätzen wissen.
Ein Window ist ein WindowFlags. Die Flags beschreiben nicht das Fenster, sondern das Fenster ist eine Interpretation der Flags. Die WindowFlags werden erweitert, so dass ein viereckiger Kasten (genannt Window) entsteht, der sich an den Flags orientiert.
Jetzt versteh ich das ueberhaupt. Das ist wirklich ein sehr interessanter Ansatz und wird doch nur wegen der Mehrfachvererbung in C++ moeglich.
Mehrfachvererbung ist SEHR cool, grade weil man Ideen beliebig zusammenfügen kann, ohne ein Interface erst implementieren zu müssen.
Und wenn man sich Gedanken macht, dass man keine virtuelle Mehrfachvererbung benutzt, ist alles easy.DEvent schrieb:
Ich weis nur nich obs Sinn macht eine "ist eine" Beziehung von einem Dialog und seinen Flags zu haben.
Jede Perspektive hat Vor- und Nachteile. Nur weil wir in der strukturierten Programmierung Flags als Teil einer Struktur begreifen mussten, muss eine Struktur als Erweiterung von Flags keine Nachteile bringen.
Die Frage ist, wo holt man die meisten Vorteile heraus.DEvent schrieb:
Xin schrieb:
Wenn operator & für zwei Fenster nicht deklariert ist, würde eine Hirachie hochgegangen und das Ergebnis wären übereinstimmende WindowFlags, genauso wie wnd1->Flags & wnd2->Flags.
Wenn Dir das nicht gefällt, deklariere operator & als private member.Was passiert wenn du von meheren Flag-Klassen ableistest?
Das passiert ja im Falle des Dialogs, der ein Window ist (also auch WindowFlags) und zusätzlich eigene DialogFlags hat.
Im Posting an CStoll sind Beispiele zu Window und Dialog.
-
Sobald man eine 2. Klasse an Flags einführt, die zB das Backbuffering oder das Thread-Verhalten betrifft, macht dieser Ansatz *bumm*.
er ist halt nicht erweiterbar und im prinzip geht es dabei nur um die ersparung von schreibarbeit. Das ganze wird dann durch etwaige Syntaxprobleme, man denke nur mal daran dass Fenster oft nur Zeiger sind, erschwert.
Es klappt zwar und erspart ne Menge schreibarbeit - aber im endeffekt verwirrt es den Leser:
void show() { if(*this & Modal) { } else if(*this & Fullscreen) { } }und bedeutet ein enormes Problem bezüglich zukünftiger Erweiterungen:
window.Mode = Fullscreen; window.Buffering = DoubleBuffering;das ganze mit der "eleganten Syntax":
window = Fullscreen; //aja - das verwirrt garantiert nicht window = DoubleBuffering; //syntaktisch möglich - aber wen verwirrt das nicht?weiters dann das checken:
if(win->Mode & Fullscreen) if(win->Buffering & DoubleBuffering)mit der "eleganten Syntax"
if(*win & Fullscreen) if(*win & BackBuffering)Das Argument mit
win->Windows::Flags & Modalist etwas fragwürdig. Erstmal der Name "flags" ist furchtbar - aber ok.
Wenn ich nun so dumm bin und die Flags von Windows und Dialog so trenne - dann habe ich das selbe Problem mit der "eleganten Syntax". Wenn Modal ein Flag von Dialog ist und Fullscreen einer von Dialog, dann ist sowas schon ziemlich dirty zu implementieren:
win = Model | Fullscreen;natürlich möglich - aber dann wird die implementierung langsam häßlich.
Man muss hier natürlich zwischen enum und einem Klassen Ansatz trennen - worum es hier geht ist, dass diese Vererbung gefährlich sein kann.
Dieser Beitrag ist übrigens an die Leser dieses Threads gerichtet - damit sie hier nicht auf dumme Ideen kommen.