Methoden mit bool-Parametern und "Lesbarkeit" im Kontext



  • Xin schrieb:

    Ich habe übrigens ein Argument gegen die mehrfache Verwendung, das ihr noch nicht habt.

    CStoll schrieb:

    Das Problem ist dabei auch die unterschiedliche Bedeutung der Anweisungsfolgen, je nach beteiligten Werten:

    window=WF::Fullscreen;
    window=WF::SysMenu;
    

    ->Window hat nur SysMenu-Stil

    Erstaunlich. Das ist überzeugend, das wäre bei

    window->Flags = WF::Fullscreen;
    window->Flags = WF::SysMenu;
    

    natürlich ganz anders.

    Unfähigkeit ist kein Argument gegen eine Technik, sondern ein Argument gegen Programmierung. Wer unfähig ist, wird Fehler unabhängig von der Technik prodizieren.

    Aber bei der Variante mit expliziten Flags sieht man, ob man nun das selbe Flag beeinflusst/überschreibt oder nicht:

    vergleich mal:

    dlg = Dialog::Fullscreen;//ist eigentlich WindowFlag::Fullscreen
    dlg = Dialog::Modal;//ist DialogFlag::Modal
    

    vs.

    dlg.wFlag = Fullscreen;
    dlg.dFlag = Modal;
    

    CStoll schrieb:

    Und nun rudert er selber wieder zurück - auch interessant zu sehen 😃 (Fakt ist - du hast den op= und damit wird er auch verwendet).

    Steine liegen am Wegrand herum. Trotzdem werden sie selten verwendet, um auf andererleuts Köpfen einzuschlagen. Du hast den Stein und damit wird er auch verwendet?

    Kennst du Murphy's Gesetz? Wenn du einen Operator anbietest, wird auch irgendjemand auf die glorreiche Idee kommen, ihn zu verwenden. Und selbst wenn du so diszipliniert bist, nach der Initialisierung des Fensters nur noch über op|= und op& die Flags zu verwenden, wirst du das kaum von jedem deiner Kollegen voraussetzen können.

    CStoll schrieb:

    PS: Ich hätte zwei Möglichkeiten anzubieten:
    Verwendung von Membern - bei "wnd.flag = ..." ist klar, was es darstellen soll

    Die Tatsache, dass ich etwas anderes mache, bedeutet ja nicht, dass ich dies hier ablehne. Ich habe eine Alternative vorgeschlagen und das hier nicht verboten.

    CStoll schrieb:

    Vererbung, aber dann ohne Operatoren, sondern mit Methoden wie : "wnd.SetFlag(Fullscreen)", "wnd.ResetFlag(FullScreen)", "wnd.HasFlag(FullScreen)"
    Beides vermeidet die Unstimmigkeiten, die dein Design heraufbeschwört.

    Und die zweite Möglichkeit ist genau das, was ich mache. Nur heißt operator & bei Dir HasFlag usw.
    Ich möchte ja nicht drauf rumreiten, aber vielleicht fällt Dir grade auf, dass Du gegen Dich selbst argumentiert, denn das Umbennenen von Funktionen ändert eine Technik nicht.

    Der Unterschied liegt in der Semantik - op& hat eine vordefinierte Bedeutung, die jeder Anwender "versteht" (und bei der man erst einmal überlegen muß, was sie mit dem Problem zu tun hat), HasFlag() drückt durch seinen Namen klar aus, was es für eine Aufgabe hat.
    (und auch auf die Gefahr, mich zu wiederholen, ein Fenster "ist" kein Flag, sondern bestenfalls ein Flagbares Objekt)

    Shade Of Mine schrieb:

    window = window2;
    

    Gefällt mir am besten 🙂

    Dir sollte man auch keine Steine in die Hand geben.

    Wenn du Angst um deine Gesundheit hast, dann solltest du alle erreichbaren Steine schnell wegschließen. Oder würdest du eine geladene Pistole auf dem Tisch liegen lassen, nur mit einem Zettel "bitte nicht anfassen" geschützt?

    Shade Of Mine schrieb:

    Und dass wir immer noch keine Lösung für

    if(win)
    

    ist auch toll.

    Prüft es ob generell Flags gesetzt sind? Oder prüft es ob das Window in einem "guten" Zustand ist bzw. offen ist?

    Zum Einen: Die Frage habe ich Dir schonmal beantwortet, Du wiederholst Dich.
    Zum anderen: Die Frage ist nicht intelligenter geworden, denn (nochmals) erstens: es ist in der Regel unsinnig 32 unabhängig Fragen gleichzeitig zu stellen und nur abhängig davon zu reagieren, ob alle Fragen die Antwort "Nein" liefern.

    Es ist möglich - und irgendwer wird so clever sein, es auch zu machen (und wenn die Window-Klasse nicht explizit dagegensteuert, wird kein Compiler der Welt so einen Müll verhindern)

    Shade Of Mine schrieb:

    Natürlich kann man diese Situationen fest definieren und den Anwender des Codes zwingen es sich zu merken.

    Der Punkt ist nicht, dass es zuviele Antworten gibt - es gibt keine, also musst Du Deine Frage vernünftig formulieren.

    Nein, der Punkt ist, daß du die Semantik des Programms hinter (für Außenstehende) unplausiblen Operatoren und impliziten Typumwandlungen versteckst, statt dem Kind einen Namen zu geben.

    Und? Wenn Du eine Klasse ableitest, guckst Du nicht, wovon Du ableitest, was Du überschreiben solltest und was Du auf keinen Fall überschreiben solltest. Und wenn Du mit Steinen um Dich wirfst, guckst Du auch nicht wohin?
    Dann solltest Du Kieshändler werden. 😉

    Da sind wir wieder bei der Disziplin der Programmierer - du machst es dir einfach, deine Flag-Klasse zu erstellen. Aber jeder, der irgendwas an deinem Framework machen will, muß die Existenz der Klasse bedenken und ein halbes Dutzend Hilfsmethoden implementieren, die nichts machen außer die Anwendbarkeit dieser Flag-Klasse(n) sicherzustellen.

    So, nun mal ein brauchbares Argument gegen die mehrfache Verwendung von Flag-Klassen. Ich brauchte bisher nicht mehrere davon in einem Objekt. Ich habe eben nochmal damit experimentiert, damit ich mich hier nichts falsches erzähle und folgendes passierte: GCC kompiliert es nicht.

    Die Fehlermeldung lautet: der Operatoraufruf ist mehrdeutig. Er ist aber nicht mehrdeutig, es ist sogar sehr eindeutig. Und der Compiler stimmt mir auch zu, benutzt man globale Operatoren, ist auf einmal alles wieder eindeutig, obwohl der zuvor "mehrdeutige" Code nicht verändert wurde.

    Wenn schon der Compiler dir erklärt, daß du Unsinn baust, solltest du es langsam einsehen 😉

    Soweit ich weiß, gibt es zwischen den beiden Möglichkeiten, Operatoren zu definieren, keinen semantischen Unterschied. Entweder liege ich da falsch oder der GCC. Grundsätzlich vertraue ich dem Compiler, aber der GCC hätte mein Vertauen nicht zum ersten Mal enttäuscht.

    Es gibt einen Unterschied zwischen beidem - die Art der Überladungs-Auflösung (das kommen bei Methoden die unterschiedlichen Scopes der beteiligten Klassen dazu, die der Compiler auch mit berücksichtigen muß).

    Ich werde also noch etwas recherchieren, wer von uns beiden hier recht hat. Egal, wer recht hat, der GCC frisst es zur Zeit nicht, was ein klares Argument dafür ist, bei mehrfacher Verwendung von Flagklassen, diese als Member zu verwenden.

    Und schon wird dein Design inkonsequent - alleinstehende Flags als Basisklasse, kombinierte Flags als Member (und wenn du die Klasse später erweitern willst, mußt du sie komplett umbauen, was nicht gerade angenehm für den Verwender ist). Dann habe ich lieber ein konsequentes Design: Alle Flag's sind Member.



  • CStoll schrieb:

    Aber bei der Variante mit expliziten Flags sieht man, ob man nun das selbe Flag beeinflusst/überschreibt oder nicht:

    vergleich mal:

    dlg = Dialog::Fullscreen;//ist eigentlich WindowFlag::Fullscreen
    dlg = Dialog::Modal;//ist DialogFlag::Modal
    

    vs.

    dlg.wFlag = Fullscreen;
    dlg.dFlag = Modal;
    

    Ehrlich gesagt, benutze ich Klassen, weil mich die Implementierung nach der Implementierung nicht mehr interessiert. Von daher ist mir ehrlich gesagt egal, ob Fullscreen jetzt in wFlag oder dFlag gespeichert wird, solange der Compiler es richtig zuordnen kann.
    Das Objekt wird beeinflusst und das wird korrekt umgesetzt. Mehr interessiert mich nicht. Ich habe keine Lust mir zu merken, ob ich DialogFlag::Modal jetzt dFlag oder wFlag oder xFlag zuordnen muss. Das weiß der Compiler besser als ich.

    CStoll schrieb:

    Kennst du Murphy's Gesetz? Wenn du einen Operator anbietest, wird auch irgendjemand auf die glorreiche Idee kommen, ihn zu verwenden. Und selbst wenn du so diszipliniert bist, nach der Initialisierung des Fensters nur noch über op|= und op& die Flags zu verwenden, wirst du das kaum von jedem deiner Kollegen voraussetzen können.

    Murphys Computergesetze halte ich für das wichtigste Informatik-Fachbuch überhaupt.

    Flags sind Grundlagen. Ich setze Grundlagen grundsätzlich voraus. Und wenn sie nicht da sind, setzt man sich zusammen und erklärt sie. Wenn ich mit Fachinformatikern oder Diplom-Informatikern arbeite, sehe ich es nicht ein, Sourcecodes zu schreiben, die "Hello World"-Niviau haben. Von denen erwarte ich ein Grundverständnis.

    CStoll schrieb:

    Der Unterschied liegt in der Semantik - op& hat eine vordefinierte Bedeutung, die jeder Anwender "versteht" (und bei der man erst einmal überlegen muß, was sie mit dem Problem zu tun hat), HasFlag() drückt durch seinen Namen klar aus, was es für eine Aufgabe hat.
    (und auch auf die Gefahr, mich zu wiederholen, ein Fenster "ist" kein Flag, sondern bestenfalls ein Flagbares Objekt)

    operator & hat bei den Flags eine klare Bedeutung. Ich habe den operator ja nicht aus Spaß ausgewählt oder wie kommst Du darauf, dass derartiges nicht verstanden würde?
    Wie löst Du denn Flags sonst auf? Und erzähl mir jetzt nix von HasFlag...

    Flags sind flagbares Objekte. Fenster sind Flagbare Objekte. Hier besteht eine Verwandtschaft, die man bei guten Design sogar ausnutzen muss.
    Fenster hatten immer Flags (hat ein). Wieviele OS haben wir denn zur Auswahl, die mit OOP arbeiten!?
    Wieviele Programmierer bestehen auf die (hat ein)-Beziehung, nur weil das schon immer so gemacht wurde?

    CStoll schrieb:

    Shade Of Mine schrieb:

    Und dass wir immer noch keine Lösung für

    if(win)
    

    ist auch toll.

    Prüft es ob generell Flags gesetzt sind? Oder prüft es ob das Window in einem "guten" Zustand ist bzw. offen ist?

    Zum Einen: Die Frage habe ich Dir schonmal beantwortet, Du wiederholst Dich.
    Zum anderen: Die Frage ist nicht intelligenter geworden, denn (nochmals) erstens: es ist in der Regel unsinnig 32 unabhängig Fragen gleichzeitig zu stellen und nur abhängig davon zu reagieren, ob alle Fragen die Antwort "Nein" liefern.

    Es ist möglich - und irgendwer wird so clever sein, es auch zu machen (und wenn die Window-Klasse nicht explizit dagegensteuert, wird kein Compiler der Welt so einen Müll verhindern)

    Sorry, das ist nicht mein Job. Wenn ein Entwickler hingeht und für seine "class DeppenWindow : public Window", einen operator bool() implementiert, dann kann und will ich das nicht verhindern.
    Ich implementiere WindowFlags und Window und da wird es keinen operator bool() geben.

    Dass jemand anderer hingehen könnte, und seine Klasse von seinen KlassenFlags ableitet und dabei so einen Mist programmiert, liegt ebenfalls nicht in meiner persönlichen Verantwortung. Man kann vieles verkehrt machen, aber deswegen ist ein richtiger Weg, der einen ähnlichen Weg geht, nicht falsch.

    CStoll schrieb:

    Da sind wir wieder bei der Disziplin der Programmierer - du machst es dir einfach, deine Flag-Klasse zu erstellen. Aber jeder, der irgendwas an deinem Framework machen will, muß die Existenz der Klasse bedenken und ein halbes Dutzend Hilfsmethoden implementieren, die nichts machen außer die Anwendbarkeit dieser Flag-Klasse(n) sicherzustellen.

    Keine Ahnung, wie Du auf ein Dutzend kommst, bei mir geht das recht locker.

    CStoll schrieb:

    So, nun mal ein brauchbares Argument gegen die mehrfache Verwendung von Flag-Klassen. Ich brauchte bisher nicht mehrere davon in einem Objekt. Ich habe eben nochmal damit experimentiert, damit ich mich hier nichts falsches erzähle und folgendes passierte: GCC kompiliert es nicht.

    Die Fehlermeldung lautet: der Operatoraufruf ist mehrdeutig. Er ist aber nicht mehrdeutig, es ist sogar sehr eindeutig. Und der Compiler stimmt mir auch zu, benutzt man globale Operatoren, ist auf einmal alles wieder eindeutig, obwohl der zuvor "mehrdeutige" Code nicht verändert wurde.

    Wenn schon der Compiler dir erklärt, daß du Unsinn baust, solltest du es langsam einsehen 😉

    Der GCC erzählt viel wenn der Tag lang ist und das wäre nicht das erste Mal, dass sich die Geschichte mit der Versionsnummer ändert. Und er ist da nicht der einzige, selbiges gilt für Java, sogar für meinen innig geliebten SAS/C.
    Was das angeht, werde ich mir das im Hinterkopf behalten und bei Gelegenheit mit anderen Compilern testen und versuche die Begründung zu finden, wieso er das für mehrdeutig erklärt, wenn es einen eindeutig passenden und einen eindeutig nicht passenden Operator dafür gibt.
    Was sowas angeht, vertraue ich erstmal auf meinen C++-Compiler im Kopf und der wirft nach eingehender Prüfung immernoch ein OK raus.

    CStoll schrieb:

    Soweit ich weiß, gibt es zwischen den beiden Möglichkeiten, Operatoren zu definieren, keinen semantischen Unterschied. Entweder liege ich da falsch oder der GCC. Grundsätzlich vertraue ich dem Compiler, aber der GCC hätte mein Vertauen nicht zum ersten Mal enttäuscht.

    Es gibt einen Unterschied zwischen beidem - die Art der Überladungs-Auflösung (das kommen bei Methoden die unterschiedlichen Scopes der beteiligten Klassen dazu, die der Compiler auch mit berücksichtigen muß).

    Du weißt nicht zufällig eine detailierte Beschreibung dazu, damit ich gucken kann, ob das auf mein Problem zutrifft und wenn ja, warum.

    CStoll schrieb:

    Ich werde also noch etwas recherchieren, wer von uns beiden hier recht hat. Egal, wer recht hat, der GCC frisst es zur Zeit nicht, was ein klares Argument dafür ist, bei mehrfacher Verwendung von Flagklassen, diese als Member zu verwenden.

    Und schon wird dein Design inkonsequent - alleinstehende Flags als Basisklasse, kombinierte Flags als Member (und wenn du die Klasse später erweitern willst, mußt du sie komplett umbauen, was nicht gerade angenehm für den Verwender ist). Dann habe ich lieber ein konsequentes Design: Alle Flag's sind Member.

    Mein Design wird nicht inkonsequent, mein Design lässt sich mit dem GCC nur dann kompilieren, wenn die Operatoren global definiert sind. Ich selbst, werde eher die Operatoren global definieren, als das Design aufgeben.
    Und ich muss wissen, ob es sich hier um einen Fehler im GCC handelt, oder um ein "Sprachfeature". Wenn es ein Feature ist, muss ich wissen, warum die C++ derartiges einschränkt. Und wenn der Grund gut ist, kann ich das gut akzeptieren.
    Aber da sind noch einige "wenn"s zu klären, bis dahin ist alles Halbwissen und Halbwissen ist wesentlich gefährlicher als ein Design, dass sich nicht kompilieren lässt, weil der Compiler einen Fehler enthält.



  • Xin schrieb:

    CStoll schrieb:

    Aber bei der Variante mit expliziten Flags sieht man, ob man nun das selbe Flag beeinflusst/überschreibt oder nicht:

    vergleich mal:

    dlg = Dialog::Fullscreen;//ist eigentlich WindowFlag::Fullscreen
    dlg = Dialog::Modal;//ist DialogFlag::Modal
    

    vs.

    dlg.wFlag = Fullscreen;
    dlg.dFlag = Modal;
    

    Ehrlich gesagt, benutze ich Klassen, weil mich die Implementierung nach der Implementierung nicht mehr interessiert. Von daher ist mir ehrlich gesagt egal, ob Fullscreen jetzt in wFlag oder dFlag gespeichert wird, solange der Compiler es richtig zuordnen kann.

    Dir ist es vielleicht egal, aber ich will schon gerne wissen, welche Flags-Zuweisungen sich jetzt gegenseitig beeinflussen. Und bei der oberen Variante muß man erst die Herkunft der Bezeichner heraussuchen, um zu erkennen, ob die Sequenz erlaubt ist oder nicht.

    CStoll schrieb:

    Kennst du Murphy's Gesetz? Wenn du einen Operator anbietest, wird auch irgendjemand auf die glorreiche Idee kommen, ihn zu verwenden. Und selbst wenn du so diszipliniert bist, nach der Initialisierung des Fensters nur noch über op|= und op& die Flags zu verwenden, wirst du das kaum von jedem deiner Kollegen voraussetzen können.

    Murphys Computergesetze halte ich für das wichtigste Informatik-Fachbuch überhaupt.

    Flags sind Grundlagen. Ich setze Grundlagen grundsätzlich voraus. Und wenn sie nicht da sind, setzt man sich zusammen und erklärt sie. Wenn ich mit Fachinformatikern oder Diplom-Informatikern arbeite, sehe ich es nicht ein, Sourcecodes zu schreiben, die "Hello World"-Niviau haben. Von denen erwarte ich ein Grundverständnis.

    "Flags" als Konzept sind Grundlagen, dein Verständnis von Flags ist dagegen erklärungsbedürftig.

    CStoll schrieb:

    Der Unterschied liegt in der Semantik - op& hat eine vordefinierte Bedeutung, die jeder Anwender "versteht" (und bei der man erst einmal überlegen muß, was sie mit dem Problem zu tun hat), HasFlag() drückt durch seinen Namen klar aus, was es für eine Aufgabe hat.
    (und auch auf die Gefahr, mich zu wiederholen, ein Fenster "ist" kein Flag, sondern bestenfalls ein Flagbares Objekt)

    operator & hat bei den Flags eine klare Bedeutung. Ich habe den operator ja nicht aus Spaß ausgewählt oder wie kommst Du darauf, dass derartiges nicht verstanden würde?
    Wie löst Du denn Flags sonst auf? Und erzähl mir jetzt nix von HasFlag...

    Ich kombiniere Flags schon über & und |, wenn ich das brauche, aber ich leite die Klassen, die damit arbeiten, nicht von der Flag-Klasse ab.

    Flags sind flagbares Objekte. Fenster sind Flagbare Objekte. Hier besteht eine Verwandtschaft, die man bei guten Design sogar ausnutzen muss.
    Fenster hatten immer Flags (hat ein). Wieviele OS haben wir denn zur Auswahl, die mit OOP arbeiten!?

    Nein, Flags sind nicht flagbar, bestenfalls kombinierbar. Und eine "hat-ein" Beziehung wird nicht durch öffentliche Vererbung abgebildet.

    CStoll schrieb:

    Shade Of Mine schrieb:

    Und dass wir immer noch keine Lösung für

    if(win)
    

    ist auch toll.

    Prüft es ob generell Flags gesetzt sind? Oder prüft es ob das Window in einem "guten" Zustand ist bzw. offen ist?

    Zum Einen: Die Frage habe ich Dir schonmal beantwortet, Du wiederholst Dich.
    Zum anderen: Die Frage ist nicht intelligenter geworden, denn (nochmals) erstens: es ist in der Regel unsinnig 32 unabhängig Fragen gleichzeitig zu stellen und nur abhängig davon zu reagieren, ob alle Fragen die Antwort "Nein" liefern.

    Es ist möglich - und irgendwer wird so clever sein, es auch zu machen (und wenn die Window-Klasse nicht explizit dagegensteuert, wird kein Compiler der Welt so einen Müll verhindern)

    Sorry, das ist nicht mein Job. Wenn ein Entwickler hingeht und für seine "class DeppenWindow : public Window", einen operator bool() implementiert, dann kann und will ich das nicht verhindern.

    WindowFlag hat/braucht einen 'operator bool' (oder vergleichbares), sonst kannst du deine Aufrufe "if(wnd&Window::Fullscreen);" nicht umsetzen - und solange abgeleitete Klassen den nicht (privat) überschreiben, erben sie diesen Operator.

    Ich implementiere WindowFlags und Window und da wird es keinen operator bool() geben.

    Was dann? Bzw. wie willst du sonst einzelne Flags abfragen?

    Dass jemand anderer hingehen könnte, und seine Klasse von seinen KlassenFlags ableitet und dabei so einen Mist programmiert, liegt ebenfalls nicht in meiner persönlichen Verantwortung. Man kann vieles verkehrt machen, aber deswegen ist ein richtiger Weg, der einen ähnlichen Weg geht, nicht falsch.

    Wieso setzt du dann einen halb ausgereiften Ansatz hierhin, wenn du doch niemanden davon profitieren lassen willst? Du stellst hier ein Grundgerüst zur Verfügung und andere Programmierer erwarten, daß dieses ohne größere Komplikationen (und wenn's geht, ohne überhaupt beachtet zu werden) verwendet werden kann.

    CStoll schrieb:

    Da sind wir wieder bei der Disziplin der Programmierer - du machst es dir einfach, deine Flag-Klasse zu erstellen. Aber jeder, der irgendwas an deinem Framework machen will, muß die Existenz der Klasse bedenken und ein halbes Dutzend Hilfsmethoden implementieren, die nichts machen außer die Anwendbarkeit dieser Flag-Klasse(n) sicherzustellen.

    Keine Ahnung, wie Du auf ein Dutzend kommst, bei mir geht das recht locker.

    Grob geschätzt - die Blattklassen brauchen auf jeden Fall einen op= für jede Flag-Klasse, die weiter oben in der Hierarchie verwendet wurde (der Compiler erzeugt deinen eigenen op= und überdeckt damit alle eventuell geerbten) und einen privaten operator bool(), um Anweisungen wie "if(win)..." abzufangen, was sonst noch nötig ist, will ich gar nicht wissen.

    CStoll schrieb:

    So, nun mal ein brauchbares Argument gegen die mehrfache Verwendung von Flag-Klassen. Ich brauchte bisher nicht mehrere davon in einem Objekt. Ich habe eben nochmal damit experimentiert, damit ich mich hier nichts falsches erzähle und folgendes passierte: GCC kompiliert es nicht.

    Die Fehlermeldung lautet: der Operatoraufruf ist mehrdeutig. Er ist aber nicht mehrdeutig, es ist sogar sehr eindeutig. Und der Compiler stimmt mir auch zu, benutzt man globale Operatoren, ist auf einmal alles wieder eindeutig, obwohl der zuvor "mehrdeutige" Code nicht verändert wurde.

    Wenn schon der Compiler dir erklärt, daß du Unsinn baust, solltest du es langsam einsehen 😉

    Der GCC erzählt viel wenn der Tag lang ist und das wäre nicht das erste Mal, dass sich die Geschichte mit der Versionsnummer ändert. Und er ist da nicht der einzige, selbiges gilt für Java, sogar für meinen innig geliebten SAS/C.
    Was das angeht, werde ich mir das im Hinterkopf behalten und bei Gelegenheit mit anderen Compilern testen und versuche die Begründung zu finden, wieso er das für mehrdeutig erklärt, wenn es einen eindeutig passenden und einen eindeutig nicht passenden Operator dafür gibt.

    Was sowas angeht, vertraue ich erstmal auf meinen C++-Compiler im Kopf und der wirft nach eingehender Prüfung immernoch ein OK raus.

    Ich bin inzwischen zu der Erkenntnis gekommen, daß die heutigen Compiler mehr von Standard C++ verstehen als ich - und deshalb dürfte ein Fehler zu 99% auf deiner Seite liegen (es sei denn, du kennst den ANSI-Standard auswendig).

    CStoll schrieb:

    Soweit ich weiß, gibt es zwischen den beiden Möglichkeiten, Operatoren zu definieren, keinen semantischen Unterschied. Entweder liege ich da falsch oder der GCC. Grundsätzlich vertraue ich dem Compiler, aber der GCC hätte mein Vertauen nicht zum ersten Mal enttäuscht.

    Es gibt einen Unterschied zwischen beidem - die Art der Überladungs-Auflösung (das kommen bei Methoden die unterschiedlichen Scopes der beteiligten Klassen dazu, die der Compiler auch mit berücksichtigen muß).

    Du weißt nicht zufällig eine detailierte Beschreibung dazu, damit ich gucken kann, ob das auf mein Problem zutrifft und wenn ja, warum.

    Leider nein, aber versuch's mal im Standard.
    (ohne Garantie: Bei der Überladungsauflösung wird zuerst der Scope gesucht, der in Frage kommen könnte und danach aus allen Funktionen aus diesem Scope die ausgewählt, die am besten passt - bei dir dürfte schon der erste Schritt scheitern)

    CStoll schrieb:

    Ich werde also noch etwas recherchieren, wer von uns beiden hier recht hat. Egal, wer recht hat, der GCC frisst es zur Zeit nicht, was ein klares Argument dafür ist, bei mehrfacher Verwendung von Flagklassen, diese als Member zu verwenden.

    Und schon wird dein Design inkonsequent - alleinstehende Flags als Basisklasse, kombinierte Flags als Member (und wenn du die Klasse später erweitern willst, mußt du sie komplett umbauen, was nicht gerade angenehm für den Verwender ist). Dann habe ich lieber ein konsequentes Design: Alle Flag's sind Member.

    Mein Design wird nicht inkonsequent, mein Design lässt sich mit dem GCC nur dann kompilieren, wenn die Operatoren global definiert sind. Ich selbst, werde eher die Operatoren global definieren, als das Design aufgeben.
    Und ich muss wissen, ob es sich hier um einen Fehler im GCC handelt, oder um ein "Sprachfeature". Wenn es ein Feature ist, muss ich wissen, warum die C++ derartiges einschränkt. Und wenn der Grund gut ist, kann ich das gut akzeptieren.
    Aber da sind noch einige "wenn"s zu klären, bis dahin ist alles Halbwissen und Halbwissen ist wesentlich gefährlicher als ein Design, dass sich nicht kompilieren lässt, weil der Compiler einen Fehler enthält.

    Oder ein Design, das einfach von der falschen Seite an ein Problem herangeht 😉



  • so gehts natürlich auch, erst dämlich anpflaumen und dann rumkneifen 🙄



  • CStoll schrieb:

    Xin schrieb:

    CStoll schrieb:

    Aber bei der Variante mit expliziten Flags sieht man, ob man nun das selbe Flag beeinflusst/überschreibt oder nicht:

    vergleich mal:

    dlg = Dialog::Fullscreen;//ist eigentlich WindowFlag::Fullscreen
    dlg = Dialog::Modal;//ist DialogFlag::Modal
    

    vs.

    dlg.wFlag = Fullscreen;
    dlg.dFlag = Modal;
    

    Ehrlich gesagt, benutze ich Klassen, weil mich die Implementierung nach der Implementierung nicht mehr interessiert. Von daher ist mir ehrlich gesagt egal, ob Fullscreen jetzt in wFlag oder dFlag gespeichert wird, solange der Compiler es richtig zuordnen kann.

    Dir ist es vielleicht egal, aber ich will schon gerne wissen, welche Flags-Zuweisungen sich jetzt gegenseitig beeinflussen. Und bei der oberen Variante muß man erst die Herkunft der Bezeichner heraussuchen, um zu erkennen, ob die Sequenz erlaubt ist oder nicht.

    Jedes Flag ist eine unabhängige Frage. Ob die sich beeinflussen oder nicht, merkst Du an den Flags nicht.

    CStoll schrieb:

    Flags sind Grundlagen. Ich setze Grundlagen grundsätzlich voraus. Und wenn sie nicht da sind, setzt man sich zusammen und erklärt sie. Wenn ich mit Fachinformatikern oder Diplom-Informatikern arbeite, sehe ich es nicht ein, Sourcecodes zu schreiben, die "Hello World"-Niviau haben. Von denen erwarte ich ein Grundverständnis.

    "Flags" als Konzept sind Grundlagen, dein Verständnis von Flags ist dagegen erklärungsbedürftig.

    Flags sind Flags. Ich gehe eigentlich davon aus, kein erklärungsbedürftiges Verständnis davon zu haben.

    CStoll schrieb:

    CStoll schrieb:

    Der Unterschied liegt in der Semantik - op& hat eine vordefinierte Bedeutung, die jeder Anwender "versteht" (und bei der man erst einmal überlegen muß, was sie mit dem Problem zu tun hat), HasFlag() drückt durch seinen Namen klar aus, was es für eine Aufgabe hat.
    (und auch auf die Gefahr, mich zu wiederholen, ein Fenster "ist" kein Flag, sondern bestenfalls ein Flagbares Objekt)

    operator & hat bei den Flags eine klare Bedeutung. Ich habe den operator ja nicht aus Spaß ausgewählt oder wie kommst Du darauf, dass derartiges nicht verstanden würde?
    Wie löst Du denn Flags sonst auf? Und erzähl mir jetzt nix von HasFlag...

    Ich kombiniere Flags schon über & und |

    Dann sind die Operatoren doch semantisch sinnvoll gewählt.

    CStoll schrieb:

    Flags sind flagbares Objekte. Fenster sind Flagbare Objekte. Hier besteht eine Verwandtschaft, die man bei guten Design sogar ausnutzen muss.
    Fenster hatten immer Flags (hat ein). Wieviele OS haben wir denn zur Auswahl, die mit OOP arbeiten!?

    Nein, Flags sind nicht flagbar, bestenfalls kombinierbar.

    Die Klasse Flags ist flagbar. Genau das ist ihre Funktion.

    CStoll schrieb:

    Ich implementiere WindowFlags und Window und da wird es keinen operator bool() geben.

    Was dann? Bzw. wie willst du sonst einzelne Flags abfragen?

    Die Frage ist gut... ^^
    Die Frage ist sogar so gut, dass ich mir meine Implementierung ansehen musste, weil hier irgendwas sonst nicht passen kann...
    Und da steht: in C++ habe ich einen operator bool().

    Wenn ich einen Rückzieher mache, dann mache ich das deutlicher, das sieht dann ungefähr so aus:
    Was den operator bool() angeht, habe ich mich zu weit aus dem Fenster gelehnt, weil ich hier was durcheinander geworfen habe und es jetzt auch schon wieder einige Zeit her ist, seitdem ich die Flagklassen entworfen habe.
    Ergo gilt auch das Argument von Shade, bzgl. if( win ). Ein offizielles und ernstgemeines Sorry an ihn und einen nicht ganz so ernstgemeinten Kniefall. 😉

    Wie kam ich darauf, dass ich den operator bool() verleugnete?
    Ich habe das Konzept von Genesys auf C++ übertragen, was aber so nicht geht, also habe ich in C++ operator bool() genau aus dem Grund überladen müssen. In Genesys (die Sprache, die ich entwickle) gibt es nicht nur Typen, sondern auch Typerweiterungen, die man an andere Typen anhängen kann. Die Flags ist eine solche Typerwerweiterung. Eine Erweiterung wird ge- und vererbt. Es handelt sich dabei aber um keine Form, die teilweise private, teilweise public ist. Die Kombination aus einer Klasse mit Flags überlädt nicht den einfachen operator bool(), sondern eine Kombination von Operatoren, so dass für if( win ) kein operator bool() definiert ist, für if( win & WindowFlag::Fullscreen ) die Sache aber anders aussieht.

    Diese Typerweiterungen haben ihren eigenen Namen, z.B. WindowFlags und deren Member werden nicht in den Namensraum übernommen. Window::Fullscreen existiert hier nicht, man muss also wirklich win |= WindowFlags::FullScreen schreiben.

    CStoll schrieb:

    Dass jemand anderer hingehen könnte, und seine Klasse von seinen KlassenFlags ableitet und dabei so einen Mist programmiert, liegt ebenfalls nicht in meiner persönlichen Verantwortung. Man kann vieles verkehrt machen, aber deswegen ist ein richtiger Weg, der einen ähnlichen Weg geht, nicht falsch.

    Wieso setzt du dann einen halb ausgereiften Ansatz hierhin, wenn du doch niemanden davon profitieren lassen willst? Du stellst hier ein Grundgerüst zur Verfügung und andere Programmierer erwarten, daß dieses ohne größere Komplikationen (und wenn's geht, ohne überhaupt beachtet zu werden) verwendet werden kann.

    Ich biete eine Alternative an. Die Alternative steht immernoch und sie funktioniert. Sie bedarf in C++ mehr Disziplin, als ich annahm, als ich das Posting schrieb, aber sie ist immernoch sinnvoll, auch dann, wenn operator bool() durchläuft, denn er ist aus der Vererbungshirachie zu erkennen.

    Ich schreibe hier ein Posting als Antwort auf eine Frage. Ich mache keine Doktorarbeit daraus. Und wenn ich hier Quelltext sehe, dann erwarte ich hier definitiv nicht, dass ich den einfach blind kopieren kann. Ich erwarte hier Anregung und Antworten, aber definitiv keine fertigen Lösungen.

    CStoll schrieb:

    Ich bin inzwischen zu der Erkenntnis gekommen, daß die heutigen Compiler mehr von Standard C++ verstehen als ich - und deshalb dürfte ein Fehler zu 99% auf deiner Seite liegen (es sei denn, du kennst den ANSI-Standard auswendig).

    Nein, auswendig kenne ich ihn nicht. Aber der Standard ist eine wohlüberlegte Sache und die Antwort des Compilers erscheint mir nur bedingt wohlüberlegt - in jedem Fall ist die Fehlermeldung vollkommen unpassend. Also habe ich entweder einen Aspekt der Überlegung übersehen oder es liegt ein Compilerfehler vor.
    Falls der Fehler an mir liegt, muss ich den Grund erfahren, um zu verstehen, warum das nicht geht. Anderenfalls muss ich wissen, dass es ein Compilerfehler ist, um sicher zu sein, dass es nicht an mir liegt.

    CStoll schrieb:

    (ohne Garantie: Bei der Überladungsauflösung wird zuerst der Scope gesucht, der in Frage kommen könnte und danach aus allen Funktionen aus diesem Scope die ausgewählt, die am besten passt - bei dir dürfte schon der erste Schritt scheitern)

    Zwei operator &-Funktionen stehen zur Auswahl. Eine Signatur passt perfekt, die andere passt überhaupt nicht.
    Und der Aufruf soll mehrdeutig sein.
    Das sagt mir auf jedenfall, dass die beide Operatoren zur Verfügung stehen und dass es ein Problem gibt zwei unterschiedliche Klassentypen auseinander zu halten, wobei nichtmals auf eine Basisklasse konvertiert werden müsste, weil der gegebene Typ explizit in der Signatur genannt wird.
    Es spielt keine Rolle, ob das mit operatoren oder normalen Funktionen probiert wird.
    Ebenfalls ist es mehrdeutig, wenn ich die Funktion mit einem "int" aufrufe und genau eine Funktion mit "int" in der Signatur habe. Der Compiler ist also nicht in der Lage ein "int" von "WindowFlag &" oder "WindowMode &" zu unterscheiden. Es ist also kein Problem, dass daher kommt, dass WindowFlag und WindowMode eine gemeinsame Basisklasse haben.

    Das Problem wird also daher kommen, dass die Überladung durch Mehrfachvererbung zustande gekommen ist.
    Und dann stellt sich die Frage, ob im GCC die Überladung durch Mehrfachererbung entweder nicht richtig implementiert wurde oder ob es eine begründete Regel gibt, derartig eindeutige Funktions-Überladungen grundsätzlich zu verbieten, selbst wenn sie eindeutig sind.
    Wäre die Qualifizierung - z.B. "WindowFlag::" - notwendig, so wäre die Fehlermeldung sinnvoll, wenn zwei identische Signaturen durch die Mehrfachvererbung zusammenkommen. Das Problem tritt hier aber nicht auf.
    Entweder nimmt der GCC die Sache zu genau - oder ich nehme die Sache zu locker.
    Dann bleibt die Frage, wer mit seiner Ansicht besser liegt.

    Viele offene Fragen...



  • Xin schrieb:

    CStoll schrieb:

    Xin schrieb:

    CStoll schrieb:

    Aber bei der Variante mit expliziten Flags sieht man, ob man nun das selbe Flag beeinflusst/überschreibt oder nicht:

    vergleich mal:

    dlg = Dialog::Fullscreen;//ist eigentlich WindowFlag::Fullscreen
    dlg = Dialog::Modal;//ist DialogFlag::Modal
    

    vs.

    dlg.wFlag = Fullscreen;
    dlg.dFlag = Modal;
    

    Ehrlich gesagt, benutze ich Klassen, weil mich die Implementierung nach der Implementierung nicht mehr interessiert. Von daher ist mir ehrlich gesagt egal, ob Fullscreen jetzt in wFlag oder dFlag gespeichert wird, solange der Compiler es richtig zuordnen kann.

    Dir ist es vielleicht egal, aber ich will schon gerne wissen, welche Flags-Zuweisungen sich jetzt gegenseitig beeinflussen. Und bei der oberen Variante muß man erst die Herkunft der Bezeichner heraussuchen, um zu erkennen, ob die Sequenz erlaubt ist oder nicht.

    Jedes Flag ist eine unabhängige Frage. Ob die sich beeinflussen oder nicht, merkst Du an den Flags nicht.

    Und genau das ist das Problem bei deinem Ansatz: Ich will den Flags ansehen, welche sich gegenseitig beeinflussen und welche nicht.

    CStoll schrieb:

    Flags sind Grundlagen. Ich setze Grundlagen grundsätzlich voraus. Und wenn sie nicht da sind, setzt man sich zusammen und erklärt sie. Wenn ich mit Fachinformatikern oder Diplom-Informatikern arbeite, sehe ich es nicht ein, Sourcecodes zu schreiben, die "Hello World"-Niviau haben. Von denen erwarte ich ein Grundverständnis.

    "Flags" als Konzept sind Grundlagen, dein Verständnis von Flags ist dagegen erklärungsbedürftig.

    Flags sind Flags. Ich gehe eigentlich davon aus, kein erklärungsbedürftiges Verständnis davon zu haben.

    Eben nicht. "Flags" als abtraktes Konzept sind (denke ich) jedem halbwegs erfahrenen Programmierer bekannt. Aber deine Sichtweise von Flags (und die Art, wie du sie umsetzt) - SIND erklärungsbedürftig (glaub's mir ruhig).

    CStoll schrieb:

    CStoll schrieb:

    Der Unterschied liegt in der Semantik - op& hat eine vordefinierte Bedeutung, die jeder Anwender "versteht" (und bei der man erst einmal überlegen muß, was sie mit dem Problem zu tun hat), HasFlag() drückt durch seinen Namen klar aus, was es für eine Aufgabe hat.
    (und auch auf die Gefahr, mich zu wiederholen, ein Fenster "ist" kein Flag, sondern bestenfalls ein Flagbares Objekt)

    operator & hat bei den Flags eine klare Bedeutung. Ich habe den operator ja nicht aus Spaß ausgewählt oder wie kommst Du darauf, dass derartiges nicht verstanden würde?
    Wie löst Du denn Flags sonst auf? Und erzähl mir jetzt nix von HasFlag...

    Ich kombiniere Flags schon über & und |

    Dann sind die Operatoren doch semantisch sinnvoll gewählt.

    Auch auf die Gefahr hin, mich zu wiederholen: An den Operatoren habe ich nichts auszusetzen - nur an der Vererbungsbeziehung (aber du kannst es dir gerne leicht machen, und meine Sätze nur bis zur Hälfte lesen :D).

    CStoll schrieb:

    Flags sind flagbares Objekte. Fenster sind Flagbare Objekte. Hier besteht eine Verwandtschaft, die man bei guten Design sogar ausnutzen muss.
    Fenster hatten immer Flags (hat ein). Wieviele OS haben wir denn zur Auswahl, die mit OOP arbeiten!?

    Nein, Flags sind nicht flagbar, bestenfalls kombinierbar.

    Die Klasse Flags ist flagbar. Genau das ist ihre Funktion.

    Kannst du in einem Flag andere Flags setzen? Nein. Ein Flag ist eine Marke, die aussagt, ob etwas eine bestimmte Eigenschaft hat. Dieses etwas ist vielleicht flagbar, aber nicht das flag selber.
    (oder willst du mir auch erzählen, daß eine Farbe färbbar ist?)

    *grübelt* und so langsam glaube ich, daß bei dir sogar das Konzept "Flag" unklar ist.

    CStoll schrieb:

    Ich implementiere WindowFlags und Window und da wird es keinen operator bool() geben.

    Was dann? Bzw. wie willst du sonst einzelne Flags abfragen?

    Die Frage ist gut... ^^
    Die Frage ist sogar so gut, dass ich mir meine Implementierung ansehen musste, weil hier irgendwas sonst nicht passen kann...
    Und da steht: in C++ habe ich einen operator bool().

    Wenn ich einen Rückzieher mache, dann mache ich das deutlicher, das sieht dann ungefähr so aus:
    Was den operator bool() angeht, habe ich mich zu weit aus dem Fenster gelehnt, weil ich hier was durcheinander geworfen habe und es jetzt auch schon wieder einige Zeit her ist, seitdem ich die Flagklassen entworfen habe.
    Ergo gilt auch das Argument von Shade, bzgl. if( win ). Ein offizielles und ernstgemeines Sorry an ihn und einen nicht ganz so ernstgemeinten Kniefall. 😉

    Da siehst du es - noch nicht einmal du selber weißt, was man mit deinen Flag-Klassen machen kann. Wie soll da noch ein anderer Programmierer, der nur deinen Code sieht, da durchblicken?

    Wie kam ich darauf, dass ich den operator bool() verleugnete?
    Ich habe das Konzept von Genesys auf C++ übertragen, was aber so nicht geht, also habe ich in C++ operator bool() genau aus dem Grund überladen müssen. In Genesys (die Sprache, die ich entwickle) gibt es nicht nur Typen, sondern auch Typerweiterungen, die man an andere Typen anhängen kann. Die Flags ist eine solche Typerwerweiterung. Eine Erweiterung wird ge- und vererbt. Es handelt sich dabei aber um keine Form, die teilweise private, teilweise public ist. Die Kombination aus einer Klasse mit Flags überlädt nicht den einfachen operator bool(), sondern eine Kombination von Operatoren, so dass für if( win ) kein operator bool() definiert ist, für if( win & WindowFlag::Fullscreen ) die Sache aber anders aussieht.

    Tja, C++ Vererbung ist halt etwas anderes als diese Typerweiterung. Und wenn du gewaltsam versuchst, ein Konzept in einer anderen Sprache nachzubilden, schaffst du damit nur Probleme. (das sieht man hier immer wieder, wenn Java-Programmierer mit C++ anfangen - oder umgekehrt)

    CStoll schrieb:

    Dass jemand anderer hingehen könnte, und seine Klasse von seinen KlassenFlags ableitet und dabei so einen Mist programmiert, liegt ebenfalls nicht in meiner persönlichen Verantwortung. Man kann vieles verkehrt machen, aber deswegen ist ein richtiger Weg, der einen ähnlichen Weg geht, nicht falsch.

    Wieso setzt du dann einen halb ausgereiften Ansatz hierhin, wenn du doch niemanden davon profitieren lassen willst? Du stellst hier ein Grundgerüst zur Verfügung und andere Programmierer erwarten, daß dieses ohne größere Komplikationen (und wenn's geht, ohne überhaupt beachtet zu werden) verwendet werden kann.

    Ich biete eine Alternative an. Die Alternative steht immernoch und sie funktioniert. Sie bedarf in C++ mehr Disziplin, als ich annahm, als ich das Posting schrieb, aber sie ist immernoch sinnvoll, auch dann, wenn operator bool() durchläuft, denn er ist aus der Vererbungshirachie zu erkennen.

    Ja, deine Alternative mag ja in Genesys der richtige Weg sein, aber in C++ ist sie immer noch falsch.

    Ich schreibe hier ein Posting als Antwort auf eine Frage. Ich mache keine Doktorarbeit daraus. Und wenn ich hier Quelltext sehe, dann erwarte ich hier definitiv nicht, dass ich den einfach blind kopieren kann. Ich erwarte hier Anregung und Antworten, aber definitiv keine fertigen Lösungen.

    Dann hoffe ich für dich, daß deine produktiven Quelltexte reichlich mit Dokumentationen versehen sind, was man mit den Klassen machen darf und was nicht (und was den Nutzer erwartet, wenn er sich nicht daran hält).

    CStoll schrieb:

    Ich bin inzwischen zu der Erkenntnis gekommen, daß die heutigen Compiler mehr von Standard C++ verstehen als ich - und deshalb dürfte ein Fehler zu 99% auf deiner Seite liegen (es sei denn, du kennst den ANSI-Standard auswendig).

    Nein, auswendig kenne ich ihn nicht. Aber der Standard ist eine wohlüberlegte Sache und die Antwort des Compilers erscheint mir nur bedingt wohlüberlegt - in jedem Fall ist die Fehlermeldung vollkommen unpassend. Also habe ich entweder einen Aspekt der Überlegung übersehen oder es liegt ein Compilerfehler vor.
    Falls der Fehler an mir liegt, muss ich den Grund erfahren, um zu verstehen, warum das nicht geht. Anderenfalls muss ich wissen, dass es ein Compilerfehler ist, um sicher zu sein, dass es nicht an mir liegt.

    CStoll schrieb:

    (ohne Garantie: Bei der Überladungsauflösung wird zuerst der Scope gesucht, der in Frage kommen könnte und danach aus allen Funktionen aus diesem Scope die ausgewählt, die am besten passt - bei dir dürfte schon der erste Schritt scheitern)

    Zwei operator &-Funktionen stehen zur Auswahl. Eine Signatur passt perfekt, die andere passt überhaupt nicht.
    Und der Aufruf soll mehrdeutig sein.

    Nicht ganz - die Funktion Window hat keinen einzigen operator& - aber es gibt zwei (auf verschiedenen Wegen) geerbte Funktionen, die passen könnten.

    Das Problem wird also daher kommen, dass die Überladung durch Mehrfachvererbung zustande gekommen ist.
    Und dann stellt sich die Frage, ob im GCC die Überladung durch Mehrfachererbung entweder nicht richtig implementiert wurde oder ob es eine begründete Regel gibt, derartig eindeutige Funktions-Überladungen grundsätzlich zu verbieten, selbst wenn sie eindeutig sind.

    Soweit ich weiß, letzteres. Aber das müsste ich auch nachschlagen.



  • Hallo CStoll,

    könntest Du statt "quote" "quote=werauchimmer" schreiben und bitte zwischen den Absätzen eine Leerzeile einfügen? Es ist ziemlich schwierig in einem Klumpen Text die Stelle zu suchen, wo man eine Antwort zwischenfügen möchte. Eine Leerzeile zwischen den Quotes hilft da ungemein. Ohne die Suchfunktion geht bei Deinen Antworten leider gar nix. Versuch bevor Du auf dieses Posting antwortest mal (ohne Abschicken) Dein Posting zu zitieren und die Stellen zu finden, wo andere Leute antworten würden. Du wirst die Zwischenzeile danach bei mir zu schätzen wissen 😉

    CStoll schrieb:

    Xin schrieb:

    Jedes Flag ist eine unabhängige Frage. Ob die sich beeinflussen oder nicht, merkst Du an den Flags nicht.

    Und genau das ist das Problem bei deinem Ansatz: Ich will den Flags ansehen, welche sich gegenseitig beeinflussen und welche nicht.

    Das ist kein Problem meines Ansatzes. Ein Flag ist eine unabhängige Ja/Nein-Frage. Ob die sich gegenseitig beeinflussen entscheidet sich nicht am Ort, wo das Flag gespeichert wird, sondern im Algorithmus, der die Flags auswertet.

    CStoll schrieb:

    CStoll schrieb:

    Ich kombiniere Flags schon über & und |

    Dann sind die Operatoren doch semantisch sinnvoll gewählt.

    Auch auf die Gefahr hin, mich zu wiederholen: An den Operatoren habe ich nichts auszusetzen - nur an der Vererbungsbeziehung (aber du kannst es dir gerne leicht machen, und meine Sätze nur bis zur Hälfte lesen :D).

    Ich habe Deine Kritik durchaus gelesen, zur Kenntnis genommen und das Thema ist subjektiver Natur. Also was soll ich dazu noch schreiben? Es gibt gute Gründe für Vererbung, wie es gute Gründe gegen Vererbung gibt.
    Du kritisiertest auch die Wahl der Operatoren, weil die ja keiner verstehen würde und wolltest HasFlags() usw. haben. Das kommentierte ich - dafür brauche ich den Rest nicht zu quoten.

    CStoll schrieb:

    CStoll schrieb:

    Nein, Flags sind nicht flagbar, bestenfalls kombinierbar.

    Die Klasse Flags ist flagbar. Genau das ist ihre Funktion.

    Kannst du in einem Flag andere Flags setzen? Nein. Ein Flag ist eine Marke, die aussagt, ob etwas eine bestimmte Eigenschaft hat. Dieses etwas ist vielleicht flagbar, aber nicht das flag selber.

    Die Klasse heißt Flags, nicht Flag. Sie repräsentiert Flags, nicht ein Flag. Und sie ist flaggbar, denn genau das ist ihre Aufgabe.

    CStoll schrieb:

    Wenn ich einen Rückzieher mache, dann mache ich das deutlicher, das sieht dann ungefähr so aus:
    Was den operator bool() angeht, habe ich mich zu weit aus dem Fenster gelehnt, weil ich hier was durcheinander geworfen habe und es jetzt auch schon wieder einige Zeit her ist, seitdem ich die Flagklassen entworfen habe.
    Ergo gilt auch das Argument von Shade, bzgl. if( win ). Ein offizielles und ernstgemeines Sorry an ihn und einen nicht ganz so ernstgemeinten Kniefall. 😉

    Da siehst du es - noch nicht einmal du selber weißt, was man mit deinen Flag-Klassen machen kann. Wie soll da noch ein anderer Programmierer, der nur deinen Code sieht, da durchblicken?

    *lach*, ich habe die Klasse jetzt seit locker einem Jahr nicht mehr angeguckt. Sie funktioniert einwandtfrei. Das Problem ist nicht, dass ich da nicht durchblicke, sondern dass ich derzeit in zwei ähnlichen Sprachwelten hänge.

    CStoll schrieb:

    Dann hoffe ich für dich, daß deine produktiven Quelltexte reichlich mit Dokumentationen versehen sind, was man mit den Klassen machen darf und was nicht (und was den Nutzer erwartet, wenn er sich nicht daran hält).

    Aufwendige Algorithmen werden selbstverständlich durchdokumentiert.

    Auch meine Klassen sind mit Doxygen dokumentiert. Aber nicht, weil sie nicht intuitiv wären, sondern weil es soviele sind, dass ich nicht immer alles im Kopf halten kann. Muss man irgendwo etwas ändern, wo man ein paar Jahre nicht mehr vorbeigeschaut hat, so ist die Dokumentation hilfreich.

    CStoll schrieb:

    Zwei operator &-Funktionen stehen zur Auswahl. Eine Signatur passt perfekt, die andere passt überhaupt nicht.
    Und der Aufruf soll mehrdeutig sein.

    Nicht ganz - die Funktion Window hat keinen einzigen operator& - aber es gibt zwei (auf verschiedenen Wegen) geerbte Funktionen, die passen könnten.

    Das kann nicht die Erklärung sein, denn das würde die Verwendung von vererbten Funktionen jeglicher Art verhindern. Es gibt zwei (auf verschiedenen Wegen) geerbte Funktionen, von denen aber eine überhaupt nicht passt und eine genau passt.
    Dass die Sprache sich unsicher gibt, ist in Ordnung, aber es ist definitiv nicht mehrdeutig. Es liegt definitiv ein Compilerfehler vor. Selbst wenn die Geschichte programmiersprachlich korrekt umgesetzt wurde, so liegt zumindest eine unsaubere Fehlermeldung vor, was ebenfalls ein Compilerfehler ist.



  • Xin schrieb:

    Hallo CStoll,

    könntest Du statt "quote" "quote=werauchimmer" schreiben und bitte zwischen den Absätzen eine Leerzeile einfügen? Es ist ziemlich schwierig in einem Klumpen Text die Stelle zu suchen, wo man eine Antwort zwischenfügen möchte. Eine Leerzeile zwischen den Quotes hilft da ungemein. Ohne die Suchfunktion geht bei Deinen Antworten leider gar nix. Versuch bevor Du auf dieses Posting antwortest mal (ohne Abschicken) Dein Posting zu zitieren und die Stellen zu finden, wo andere Leute antworten würden. Du wirst die Zwischenzeile danach bei mir zu schätzen wissen 😉

    OK, ich werde mich in Zukunft bemühen.

    Xin schrieb:

    CStoll schrieb:

    Xin schrieb:

    Jedes Flag ist eine unabhängige Frage. Ob die sich beeinflussen oder nicht, merkst Du an den Flags nicht.

    Und genau das ist das Problem bei deinem Ansatz: Ich will den Flags ansehen, welche sich gegenseitig beeinflussen und welche nicht.

    Das ist kein Problem meines Ansatzes. Ein Flag ist eine unabhängige Ja/Nein-Frage. Ob die sich gegenseitig beeinflussen entscheidet sich nicht am Ort, wo das Flag gespeichert wird, sondern im Algorithmus, der die Flags auswertet.

    Ich will es aber an dem Ort wissen, wo ich die Flags verwende - dem Code

    dlg = Dialog::Fullscreen;
    dlg = Dialog::Modal;
    

    sieht man nicht an, ob der Dialog hinterher noch im Vollbild-Modus ist oder nicht (und selbst mit expliziten Angaben WindowFlag::Fullscreen bzw. DialogFlag::Modal mußt du erstmal nachsehen, welchen Typ beides hat - ein Anwender könnte durchaus auf die Idee kommen, die DialogFlag-Klasse nur als Sammlung von zusätzlichen WindowFlag-Werten anzulegen).

    Xin schrieb:

    CStoll schrieb:

    CStoll schrieb:

    Ich kombiniere Flags schon über & und |

    Dann sind die Operatoren doch semantisch sinnvoll gewählt.

    Auch auf die Gefahr hin, mich zu wiederholen: An den Operatoren habe ich nichts auszusetzen - nur an der Vererbungsbeziehung (aber du kannst es dir gerne leicht machen, und meine Sätze nur bis zur Hälfte lesen :D).

    Ich habe Deine Kritik durchaus gelesen, zur Kenntnis genommen und das Thema ist subjektiver Natur. Also was soll ich dazu noch schreiben? Es gibt gute Gründe für Vererbung, wie es gute Gründe gegen Vererbung gibt.
    Du kritisiertest auch die Wahl der Operatoren, weil die ja keiner verstehen würde und wolltest HasFlags() usw. haben. Das kommentierte ich - dafür brauche ich den Rest nicht zu quoten.

    Genauer: Ich kritisiere das Zusammenwirken von Vererbung und Operatoren - die Operatoren alleine sind brauchbar (mit den Flags als Member), die Vererbungsbeziehung alleine auch (allerdings nicht von Flags, sondern von Flaggable aus), beides zusammen erzeugt unleserlichen - und darum gefährlichen - Code.

    Xin schrieb:

    CStoll schrieb:

    CStoll schrieb:

    Nein, Flags sind nicht flagbar, bestenfalls kombinierbar.

    Die Klasse Flags ist flagbar. Genau das ist ihre Funktion.

    Kannst du in einem Flag andere Flags setzen? Nein. Ein Flag ist eine Marke, die aussagt, ob etwas eine bestimmte Eigenschaft hat. Dieses etwas ist vielleicht flagbar, aber nicht das flag selber.

    Die Klasse heißt Flags, nicht Flag. Sie repräsentiert Flags, nicht ein Flag. Und sie ist flaggbar, denn genau das ist ihre Aufgabe.

    Wenn du das aus dieser Sicht siehst, dann sollten die konkreten Flag-Werte aber keine Objekte der Klasse "Flags" sein - diese präsentieren nämlich ein einzelnes Flag.

    Xin schrieb:

    CStoll schrieb:

    Wenn ich einen Rückzieher mache, dann mache ich das deutlicher, das sieht dann ungefähr so aus:
    Was den operator bool() angeht, habe ich mich zu weit aus dem Fenster gelehnt, weil ich hier was durcheinander geworfen habe und es jetzt auch schon wieder einige Zeit her ist, seitdem ich die Flagklassen entworfen habe.
    Ergo gilt auch das Argument von Shade, bzgl. if( win ). Ein offizielles und ernstgemeines Sorry an ihn und einen nicht ganz so ernstgemeinten Kniefall. 😉

    Da siehst du es - noch nicht einmal du selber weißt, was man mit deinen Flag-Klassen machen kann. Wie soll da noch ein anderer Programmierer, der nur deinen Code sieht, da durchblicken?

    *lach*, ich habe die Klasse jetzt seit locker einem Jahr nicht mehr angeguckt. Sie funktioniert einwandtfrei. Das Problem ist nicht, dass ich da nicht durchblicke, sondern dass ich derzeit in zwei ähnlichen Sprachwelten hänge.

    Du verstehst (zumindest rudimentär) beide Sprachwelten - andere Entwickler kennen nur eine Seite. Und da erwartest du, daß sie die Beweggründe hinter deinem Design ohne Hilfen durchschauen?

    Xin schrieb:

    Auch meine Klassen sind mit Doxygen dokumentiert. Aber nicht, weil sie nicht intuitiv wären, sondern weil es soviele sind, dass ich nicht immer alles im Kopf halten kann. Muss man irgendwo etwas ändern, wo man ein paar Jahre nicht mehr vorbeigeschaut hat, so ist die Dokumentation hilfreich.

    Daß sie dokumentiert sind, glaube ich dir, aber hast du die ganzen Fallstricke, die du auf den letzten Seiten erfolgreich ignoriert hast (ala "verwende niemals 'if(win)'" oder die möglichen Abhängigkeiten verschiedener Flags) dort ausführlich beschrieben? Wenn nein, wird ziemlich schnell jemand auf die Idee kommen, die "verbotenen" Möglichkeiten auszunutzen.

    CStoll schrieb:

    Zwei operator &-Funktionen stehen zur Auswahl. Eine Signatur passt perfekt, die andere passt überhaupt nicht.
    Und der Aufruf soll mehrdeutig sein.

    Nicht ganz - die Funktion Window hat keinen einzigen operator& - aber es gibt zwei (auf verschiedenen Wegen) geerbte Funktionen, die passen könnten.

    Das kann nicht die Erklärung sein, denn das würde die Verwendung von vererbten Funktionen jeglicher Art verhindern. Es gibt zwei (auf verschiedenen Wegen) geerbte Funktionen, von denen aber eine überhaupt nicht passt und eine genau passt.

    Keine der Funktionen passt genau - bei beiden müsste der erste Operand umgewandelt werden in eine Basisklasse.

    Achja, wo wir gerade beim Thema sind - ich hab' tatsächlich etwas passendes gefunden:

    ARM schrieb:

    Access to base class members must be unambigous. Access to a base class member is ambiguos if the expession used refers to more than one function, object, type, or enumerator. The check for ambiguity takes place before access control (§11). For example,

    class A {
    public:
      int a;
      int (*b)();
      int f();
      int f(int);
      int g();
    };
    
    class B {
      int a;
      int b();
    public:
      int f();
      int g;
      int h();
      int h(int);
    };
    
    class C : public A, public B {};
    
    void g(C* pc)
    {
      pc->a = 1;  // error: ambigous: A::a or B::a
      pc->b();    // error: ambigous: A::b or B::b
      pc->f();    // error: ambigous: A::f or B::f
      pc->f(1);   // error: ambigous: A::f or B::f
      pc->g();    // error: ambigous: A::g or B::g
      pc->g = 1;  // error: ambigous: A::g or B::g
      pc->h();    // ok
      pc->h(1);   // ok
    }
    

    If the name of an overloaded function is unambiguously found overloading resolution also takes place before access control.

    [...]

    Checking for ambiguity is done before access control or type checking (§10.1.1). Thus, an ambiguity exists when base class members have the same name, even if only one is accessible from the derived class.

    (das stammt aus dem ARM - Kapitel 10.1.1, falls du nachlesen möchtest)

    Oder auf deutsch: Zuerst wird überprüft, ob es einen eindeutigen Weg gibt, den Namen zuzuordnen, erst danach wird kontrolliert, ob er passt (betrachte den vierten Aufruf dort oben - es gibt nur einen f(int), aber er ist trotzdem mehrdeutig).

    PS: Ich hab' nochmal über die Typerweiterungen nachgedacht - und in C++ sind die wohl besser mit Konvertierungsoperatoren darstellbar:

    class WinFlag {...};
    class Window
    {
      WinFlag f;
    public:
      operator WinFlag&() {return f;}
      ...
    };
    


  • CStoll schrieb:

    Xin schrieb:

    Du wirst die Zwischenzeile danach bei mir zu schätzen wissen 😉

    OK, ich werde mich in Zukunft bemühen.

    Klappt doch super 🙂
    👍

    CStoll schrieb:

    Xin schrieb:

    CStoll schrieb:

    Xin schrieb:

    Jedes Flag ist eine unabhängige Frage. Ob die sich beeinflussen oder nicht, merkst Du an den Flags nicht.

    Und genau das ist das Problem bei deinem Ansatz: Ich will den Flags ansehen, welche sich gegenseitig beeinflussen und welche nicht.

    Das ist kein Problem meines Ansatzes. Ein Flag ist eine unabhängige Ja/Nein-Frage. Ob die sich gegenseitig beeinflussen entscheidet sich nicht am Ort, wo das Flag gespeichert wird, sondern im Algorithmus, der die Flags auswertet.

    Ich will es aber an dem Ort wissen, wo ich die Flags verwende - dem Code

    dlg = Dialog::Fullscreen;
    dlg = Dialog::Modal;
    

    sieht man nicht an, ob der Dialog hinterher noch im Vollbild-Modus ist oder nicht (und selbst mit expliziten Angaben WindowFlag::Fullscreen bzw. DialogFlag::Modal mußt du erstmal nachsehen, welchen Typ beides hat - ein Anwender könnte durchaus auf die Idee kommen, die DialogFlag-Klasse nur als Sammlung von zusätzlichen WindowFlag-Werten anzulegen).

    Das hatten wir doch schon durchgekaut: Flags setzt man nicht mit =, sondern mit |=.
    operator = wird protected, damit man die Defaultwerte setzen kann und diejenigen sicher sind, die damit nicht klarkommen.
    Zumal die Problematik sowieso erledigt ist, weil der Compiler den Operator mehrdeutig findet.

    CStoll schrieb:

    Genauer: Ich kritisiere das Zusammenwirken von Vererbung und Operatoren - die Operatoren alleine sind brauchbar (mit den Flags als Member), die Vererbungsbeziehung alleine auch (allerdings nicht von Flags, sondern von Flaggable aus), beides zusammen erzeugt unleserlichen - und darum gefährlichen - Code.

    Dann nenn die Klasse Flagable. 😉

    Aber auch hier gilt, es spielt eigentlich keine Rolle mehr, da der Compiler die Geschichte nicht akzeptiert. Ich kann noch so tolle Ideen haben, solange sie in C++ nicht kompilierbar sind, taugen sie für C++ nichts.

    Dass die Methode in C++ auch noch mehr Disziplin benötigt, als ich erwartete (genausoviel, als würde man ein Integer und Defines nehmen), macht die Sache nicht besser - von der Idee her aber auch nicht schlechter oder gefährlicher.

    CStoll schrieb:

    Xin schrieb:

    Die Klasse heißt Flags, nicht Flag. Sie repräsentiert Flags, nicht ein Flag. Und sie ist flaggbar, denn genau das ist ihre Aufgabe.

    Wenn du das aus dieser Sicht siehst, dann sollten die konkreten Flag-Werte aber keine Objekte der Klasse "Flags" sein - diese präsentieren nämlich ein einzelnes Flag.

    ...und ich dachte, ich wäre kleinlich.
    Also in Zukunft class WindowFlag mit den einzelnen Flags, das wird dann protected auf WindowFlags abgeleitet, welches dann public auf Window abgeleitet wird. Ergebnis: WindowFlag(ohne s) enthält die einzelnen Flaggen, WindowFlags verwaltet viele Flaggen und Window weiß von nichts. (Nein, ich habe es nicht ausprobiert, das kam nur grade dabei rum, dass Du die Flags und das einzelne Flag auch noch trennen möchtest. Ob und wenn ja, was für Merkwürdigkeiten dabei rumkommen, juckt mich in dem Thread weniger, weil es ja nicht kompiliert.)

    CStoll schrieb:

    Du verstehst (zumindest rudimentär) beide Sprachwelten - andere Entwickler kennen nur eine Seite. Und da erwartest du, daß sie die Beweggründe hinter deinem Design ohne Hilfen durchschauen?

    Das Design ist leicht zu durchschauen, da braucht man nichtmals guten Willen dazu.
    Wenn man natürlich operator & erst versteht, wenn man darauf aufmerksam gemacht wird, dass man das bei Integers genauso macht, dann frage ich mich, ob der ursprüngliche Wille nicht in die andere Richtung ging 😉

    CStoll schrieb:

    Daß sie dokumentiert sind, glaube ich dir, aber hast du die ganzen Fallstricke, die du auf den letzten Seiten erfolgreich ignoriert hast (ala "verwende niemals 'if(win)'" oder die möglichen Abhängigkeiten verschiedener Flags) dort ausführlich beschrieben? Wenn nein, wird ziemlich schnell jemand auf die Idee kommen, die "verbotenen" Möglichkeiten auszunutzen.

    Wer if( win.Flags ) benutzt wird sowieso gesteinigt, sofern das nicht explizit erlaubt und begründet ist. Sobald da eine Flage hinzugefügt wird, die unabhängig von den Flaggen ist, die damit abgefragt werden wollen, geht das ganze schief. if( win.Flags & KombinationAusGewünschtenFlaggen ) ist wohl Pflicht, selbst wenn das alle existierenden Flaggen sind und der Rest nicht verwendet wird und grundsätzlich auf 0 steht.

    CStoll schrieb:

    Achja, wo wir gerade beim Thema sind - ich hab' tatsächlich etwas passendes gefunden:

    ARM schrieb:

    Access to base class members must be unambigous.
    [...]

    Checking for ambiguity is done before access control or type checking (§10.1.1). Thus, an ambiguity exists when base class members have the same name, even if only one is accessible from the derived class.

    (das stammt aus dem ARM - Kapitel 10.1.1, falls du nachlesen möchtest)

    Würde ich gerne, insbesondere weil mich der Grund für diese Designentscheidung interessiert. Leider habe ich das ARM scheinbar nicht mehr, zumindest ist es verschwunden. :-\

    Du hast nicht zufällig die Begründung zum Quoten da?
    Alternativ nehme ich das ARM auch komplett 😉

    CStoll schrieb:

    PS: Ich hab' nochmal über die Typerweiterungen nachgedacht - und in C++ sind die wohl besser mit Konvertierungsoperatoren darstellbar:

    class WinFlag {...};
    class Window
    {
      WinFlag f;
    public:
      operator WinFlag&() {return f;}
      ...
    };
    

    Dem stimme ich zu.



  • Xin, hast du schon mal darüber nachgedacht Politiker zu werden?



  • Xin schrieb:

    CStoll schrieb:

    Xin schrieb:

    CStoll schrieb:

    Xin schrieb:

    Jedes Flag ist eine unabhängige Frage. Ob die sich beeinflussen oder nicht, merkst Du an den Flags nicht.

    Und genau das ist das Problem bei deinem Ansatz: Ich will den Flags ansehen, welche sich gegenseitig beeinflussen und welche nicht.

    Das ist kein Problem meines Ansatzes. Ein Flag ist eine unabhängige Ja/Nein-Frage. Ob die sich gegenseitig beeinflussen entscheidet sich nicht am Ort, wo das Flag gespeichert wird, sondern im Algorithmus, der die Flags auswertet.

    Ich will es aber an dem Ort wissen, wo ich die Flags verwende - dem Code

    dlg = Dialog::Fullscreen;
    dlg = Dialog::Modal;
    

    sieht man nicht an, ob der Dialog hinterher noch im Vollbild-Modus ist oder nicht (und selbst mit expliziten Angaben WindowFlag::Fullscreen bzw. DialogFlag::Modal mußt du erstmal nachsehen, welchen Typ beides hat - ein Anwender könnte durchaus auf die Idee kommen, die DialogFlag-Klasse nur als Sammlung von zusätzlichen WindowFlag-Werten anzulegen).

    Das hatten wir doch schon durchgekaut: Flags setzt man nicht mit =, sondern mit |=.
    operator = wird protected, damit man die Defaultwerte setzen kann und diejenigen sicher sind, die damit nicht klarkommen.
    Zumal die Problematik sowieso erledigt ist, weil der Compiler den Operator mehrdeutig findet.

    Und wozu gibt's dann die Zuweisung?

    Xin schrieb:

    CStoll schrieb:

    Genauer: Ich kritisiere das Zusammenwirken von Vererbung und Operatoren - die Operatoren alleine sind brauchbar (mit den Flags als Member), die Vererbungsbeziehung alleine auch (allerdings nicht von Flags, sondern von Flaggable aus), beides zusammen erzeugt unleserlichen - und darum gefährlichen - Code.

    Dann nenn die Klasse Flagable. 😉

    Mach ich ja, aber der Klasse Flagable würde ich keine Flag-Operatoren geben.

    Xin schrieb:

    CStoll schrieb:

    Xin schrieb:

    Die Klasse heißt Flags, nicht Flag. Sie repräsentiert Flags, nicht ein Flag. Und sie ist flaggbar, denn genau das ist ihre Aufgabe.

    Wenn du das aus dieser Sicht siehst, dann sollten die konkreten Flag-Werte aber keine Objekte der Klasse "Flags" sein - diese präsentieren nämlich ein einzelnes Flag.

    ...und ich dachte, ich wäre kleinlich.
    Also in Zukunft class WindowFlag mit den einzelnen Flags, das wird dann protected auf WindowFlags abgeleitet, welches dann public auf Window abgeleitet wird. Ergebnis: WindowFlag(ohne s) enthält die einzelnen Flaggen, WindowFlags verwaltet viele Flaggen und Window weiß von nichts. (Nein, ich habe es nicht ausprobiert, das kam nur grade dabei rum, dass Du die Flags und das einzelne Flag auch noch trennen möchtest. Ob und wenn ja, was für Merkwürdigkeiten dabei rumkommen, juckt mich in dem Thread weniger, weil es ja nicht kompiliert.)

    Wenn ich's trenne, dann in eine Klasse Flags (oder Flagable), von der Window abgeleitet wird und eine Klasse Flag - die von Flags genuzt wird (hier keine Ableitung). Und dann degeneriert Flag zu einem einfachen enum.

    Xin schrieb:

    CStoll schrieb:

    Du verstehst (zumindest rudimentär) beide Sprachwelten - andere Entwickler kennen nur eine Seite. Und da erwartest du, daß sie die Beweggründe hinter deinem Design ohne Hilfen durchschauen?

    Das Design ist leicht zu durchschauen, da braucht man nichtmals guten Willen dazu.
    Wenn man natürlich operator & erst versteht, wenn man darauf aufmerksam gemacht wird, dass man das bei Integers genauso macht, dann frage ich mich, ob der ursprüngliche Wille nicht in die andere Richtung ging 😉

    Und als nächstes leitest du Window noch von Point ab, um es mit op+= über den Monitor schieben zu können? (oder bewirkt das eine Größenänderung des Fensters?) Ja, Integers kann man ver-und-en - aber Integers verwendet man auch nicht als Basis für eigene Klassen, um diese verunden zu können.

    Xin schrieb:

    CStoll schrieb:

    Daß sie dokumentiert sind, glaube ich dir, aber hast du die ganzen Fallstricke, die du auf den letzten Seiten erfolgreich ignoriert hast (ala "verwende niemals 'if(win)'" oder die möglichen Abhängigkeiten verschiedener Flags) dort ausführlich beschrieben? Wenn nein, wird ziemlich schnell jemand auf die Idee kommen, die "verbotenen" Möglichkeiten auszunutzen.

    Wer if( win.Flags ) benutzt wird sowieso gesteinigt, sofern das nicht explizit erlaubt und begründet ist. Sobald da eine Flage hinzugefügt wird, die unabhängig von den Flaggen ist, die damit abgefragt werden wollen, geht das ganze schief. if( win.Flags & KombinationAusGewünschtenFlaggen ) ist wohl Pflicht, selbst wenn das alle existierenden Flaggen sind und der Rest nicht verwendet wird und grundsätzlich auf 0 steht.

    if(win.Flags) kann ich explizit verbieten, indem ich den Member privat mache (und nur HasFlag() etc anbiete), dein if(win) kann nur unter Aufwand verboten werden.

    Xin schrieb:

    CStoll schrieb:

    Achja, wo wir gerade beim Thema sind - ich hab' tatsächlich etwas passendes gefunden:

    ARM schrieb:

    Access to base class members must be unambigous.
    [...]

    Checking for ambiguity is done before access control or type checking (§10.1.1). Thus, an ambiguity exists when base class members have the same name, even if only one is accessible from the derived class.

    (das stammt aus dem ARM - Kapitel 10.1.1, falls du nachlesen möchtest)

    Würde ich gerne, insbesondere weil mich der Grund für diese Designentscheidung interessiert. Leider habe ich das ARM scheinbar nicht mehr, zumindest ist es verschwunden. :-\

    Du hast nicht zufällig die Begründung zum Quoten da?
    Alternativ nehme ich das ARM auch komplett 😉

    Ich hab' nochmal nachgesehen, aber eine echte Begründung dafür ist wohl gut versteckt.



  • CStoll schrieb:

    Xin schrieb:

    Das hatten wir doch schon durchgekaut: Flags setzt man nicht mit =, sondern mit |=.
    operator = wird protected, damit man die Defaultwerte setzen kann und diejenigen sicher sind, die damit nicht klarkommen.
    Zumal die Problematik sowieso erledigt ist, weil der Compiler den Operator mehrdeutig findet.

    Und wozu gibt's dann die Zuweisung?

    Um Default-Werte zu setzen, zum Beispiel bei der Konstruktion.
    Den Operator = braucht man eigentlich überhaupt nicht, höchstens um der zu flaggenden Klasse Dinge das Abschalten aller Flaggen und anschließendem Zusetzen der gewünschten Flaggen zu vereinfachen.
    Flags in Verbindung mit operator = ist immer risikobehaftet.

    CStoll schrieb:

    Xin schrieb:

    CStoll schrieb:

    Genauer: Ich kritisiere das Zusammenwirken von Vererbung und Operatoren - die Operatoren alleine sind brauchbar (mit den Flags als Member), die Vererbungsbeziehung alleine auch (allerdings nicht von Flags, sondern von Flaggable aus), beides zusammen erzeugt unleserlichen - und darum gefährlichen - Code.

    Dann nenn die Klasse Flagable. 😉

    Mach ich ja, aber der Klasse Flagable würde ich keine Flag-Operatoren geben.

    Warum nicht?

    CStoll schrieb:

    Xin schrieb:

    CStoll schrieb:

    Du verstehst (zumindest rudimentär) beide Sprachwelten - andere Entwickler kennen nur eine Seite. Und da erwartest du, daß sie die Beweggründe hinter deinem Design ohne Hilfen durchschauen?

    Das Design ist leicht zu durchschauen, da braucht man nichtmals guten Willen dazu.
    Wenn man natürlich operator & erst versteht, wenn man darauf aufmerksam gemacht wird, dass man das bei Integers genauso macht, dann frage ich mich, ob der ursprüngliche Wille nicht in die andere Richtung ging 😉

    Und als nächstes leitest du Window noch von Point ab, um es mit op+= über den Monitor schieben zu können? (oder bewirkt das eine Größenänderung des Fensters?)

    Wieder habe ich das Gefühl, dass Du keinen guten Willen mitteilen möchtest. ^
    Ich habe keine Fenster in meinem Framework, aber wenn Du mich so fragst, warum sollte ich Fenster nicht von einem Punkt ableiten? Genauer von einem Field, das ist eine positionierte(Point) Fläche (Area).
    Um ein Fenster zu verschieben setze man entweder den Punkt oder verschiebe es um eine Distance.

    Vermutlich würde ich allerdings von Bitmap(abgeleitet von einer BitmapDescription, welche von Area abgeleitet wurde) ableiten und Punkt ableiten und einen operator & Field() erstellen.

    Point ObenLinks, UntenRechts;
    class Window : public Point, public BitMap {}
    
    win = Position;                             // Wo?
    win = Area( UntenRechts - ObenLinks );      // Wie groß?
    win += Distance( 10, 10 );                  // Ein wenig verschieben.
    win.DrawBox( Point::Zero, UntenRechts );
    win.CopyFrom( CloseIcon, ObenLinks+Distance( 1, 1 ) );
    win.DrawLine( Point( 1, CloseIcon.Height+1 ) ), Point(UntenRechts.x-1, CloseIcon.Height+1 ) );
    win.DrawText( Point( CloseIcon.Width+1, 1 ), "Mein Fenster" );
    
    printf( "Fenster befindet sich an Position %s, Fläche %s und ist hübsch bemalt.\n", Point( win ).ToString(), Area( win ).ToString() );
    
    screen.CopyFrom( win, win );
    

    Fertig ist das Fenster.

    CStoll schrieb:

    Ja, Integers kann man ver-und-en - aber Integers verwendet man auch nicht als Basis für eigene Klassen, um diese verunden zu können.

    Warum eigentlich nicht?
    Grade das ist doch die Kritik derer, die das "reine OOP" in C++ suchen.

    CStoll schrieb:

    Xin schrieb:

    CStoll schrieb:

    Daß sie dokumentiert sind, glaube ich dir, aber hast du die ganzen Fallstricke, die du auf den letzten Seiten erfolgreich ignoriert hast (ala "verwende niemals 'if(win)'" oder die möglichen Abhängigkeiten verschiedener Flags) dort ausführlich beschrieben? Wenn nein, wird ziemlich schnell jemand auf die Idee kommen, die "verbotenen" Möglichkeiten auszunutzen.

    Wer if( win.Flags ) benutzt wird sowieso gesteinigt, sofern das nicht explizit erlaubt und begründet ist. Sobald da eine Flage hinzugefügt wird, die unabhängig von den Flaggen ist, die damit abgefragt werden wollen, geht das ganze schief. if( win.Flags & KombinationAusGewünschtenFlaggen ) ist wohl Pflicht, selbst wenn das alle existierenden Flaggen sind und der Rest nicht verwendet wird und grundsätzlich auf 0 steht.

    if(win.Flags) kann ich explizit verbieten, indem ich den Member privat mache (und nur HasFlag() etc anbiete), dein if(win) kann nur unter Aufwand verboten werden.

    Du packst operator bool() als privates Member in Window?

    CStoll schrieb:

    Xin schrieb:

    CStoll schrieb:

    Achja, wo wir gerade beim Thema sind - ich hab' tatsächlich etwas passendes gefunden:

    ARM schrieb:

    Access to base class members must be unambigous.
    [...]

    Checking for ambiguity is done before access control or type checking (§10.1.1). Thus, an ambiguity exists when base class members have the same name, even if only one is accessible from the derived class.

    (das stammt aus dem ARM - Kapitel 10.1.1, falls du nachlesen möchtest)

    Würde ich gerne, insbesondere weil mich der Grund für diese Designentscheidung interessiert. Leider habe ich das ARM scheinbar nicht mehr, zumindest ist es verschwunden. :-\

    Du hast nicht zufällig die Begründung zum Quoten da?
    Alternativ nehme ich das ARM auch komplett 😉

    Ich hab' nochmal nachgesehen, aber eine echte Begründung dafür ist wohl gut versteckt.

    Das ist das Problem... ich brauche keine Regeln - ich brauche Begründungen, warum Regeln existieren. Viele Regeln existieren nämlich nur, weil man das schon immer so gemacht hat.
    Das glaube ich in dem Fall nicht, aber es beantwortet nicht die Frage, warum selbst unmögliche Fälle als Möglichkeit erkannt werden, um dann zu argumentieren, dass es mehrdeutig sei.
    Sicherlich ist hier eine Gefahr, wenn unwissentlich, mehrere Funktionen gleichen Namens und unterschiedlicher Signatur zusammen kommen, dass man die Basisklassenzugehörigkeit vertauschen könnte. Hier fehlt mir die Möglichkeit, die Zusammenführung aus der Basisklasse heraus explizit zu erlauben, ohne dafür neuen Code zu produzieren.
    In meinem Fall ist die Zusammenführung ja gewünscht.

    Aber ich habe - was das angeht - auch eine unmoderne Sicht auf Programmiersprachen: Der Entwickler sollte alle Möglichkeiten haben, was natürlich dazu beiträgt, dass er auch die Verantwortung für die Möglichkeiten übernehmen muss. Das ist zur Zeit unpopulär. Nichtsdestrotrotz muss er von der Sprache soweit wie möglich unterstützt werden.



  • Xin schrieb:

    CStoll schrieb:

    Xin schrieb:

    Das hatten wir doch schon durchgekaut: Flags setzt man nicht mit =, sondern mit |=.
    operator = wird protected, damit man die Defaultwerte setzen kann und diejenigen sicher sind, die damit nicht klarkommen.
    Zumal die Problematik sowieso erledigt ist, weil der Compiler den Operator mehrdeutig findet.

    Und wozu gibt's dann die Zuweisung?

    Um Default-Werte zu setzen, zum Beispiel bei der Konstruktion.
    Den Operator = braucht man eigentlich überhaupt nicht, höchstens um der zu flaggenden Klasse Dinge das Abschalten aller Flaggen und anschließendem Zusetzen der gewünschten Flaggen zu vereinfachen.
    Flags in Verbindung mit operator = ist immer risikobehaftet.

    Und da muß man dann auch aufpassen, welche Teilmenge der Flags man nun tatsächlich erwischt.

    Xin schrieb:

    CStoll schrieb:

    Xin schrieb:

    CStoll schrieb:

    Genauer: Ich kritisiere das Zusammenwirken von Vererbung und Operatoren - die Operatoren alleine sind brauchbar (mit den Flags als Member), die Vererbungsbeziehung alleine auch (allerdings nicht von Flags, sondern von Flaggable aus), beides zusammen erzeugt unleserlichen - und darum gefährlichen - Code.

    Dann nenn die Klasse Flagable. 😉

    Mach ich ja, aber der Klasse Flagable würde ich keine Flag-Operatoren geben.

    Warum nicht?

    Die Klasse Flagable ist kein Flag, also soll sie sich auch nicht wie eins verhalten.

    Xin schrieb:

    Ich habe keine Fenster in meinem Framework, aber wenn Du mich so fragst, warum sollte ich Fenster nicht von einem Punkt ableiten? Genauer von einem Field, das ist eine positionierte(Point) Fläche (Area).

    Wenn du keine Fenster hast, warum reden wir die ganze Zeit über WindowFlags? Der Punkt, auf den ich hinauswollte, ist, daß man theoretisch alles mögliche von allem ableiten könnte - aber ob es Sinn macht, ist die andere Sache. (und bei der Beziehung Fenster->Punkt mußt du auch deutlich machen, wo das Fenster relativ zu seinen Punkt-Koordinaten liegt.

    Xin schrieb:

    Um ein Fenster zu verschieben setze man entweder den Punkt oder verschiebe es um eine Distance.

    Vermutlich würde ich allerdings von Bitmap(abgeleitet von einer BitmapDescription, welche von Area abgeleitet wurde) ableiten und Punkt ableiten und einen operator & Field() erstellen.

    Point ObenLinks, UntenRechts;
    class Window : public Point, public BitMap {}
    
    win = Position;                             // Wo?
    win = Area( UntenRechts - ObenLinks );      // Wie groß?
    win += Distance( 10, 10 );                  // Ein wenig verschieben.
    win.DrawBox( Point::Zero, UntenRechts );
    win.CopyFrom( CloseIcon, ObenLinks+Distance( 1, 1 ) );
    win.DrawLine( Point( 1, CloseIcon.Height+1 ) ), Point(UntenRechts.x-1, CloseIcon.Height+1 ) );
    win.DrawText( Point( CloseIcon.Width+1, 1 ), "Mein Fenster" );
    
    printf( "Fenster befindet sich an Position %s, Fläche %s und ist hübsch bemalt.\n", Point( win ).ToString(), Area( win ).ToString() );
    
    screen.CopyFrom( win, win );
    

    Und wer sagt mir, daß operator+= nicht die Fenstergröße korrigiert? Auf den ersten Blick mag das ja ganz nett sein, aber mir ist es trotzdem lieber zu wissen, worauf sich eine Zuweisung bezieht.

    Um mal vom Fenster-Beispiel wegzukommen: Würdest du eine Klasse Auto von Farbe, Höchstgeschwindigkeit, Leistung oder aktueller Geschwindigkeit ableiten, nur um "a=rot;" (umlackieren) oder "a+=10;" (beschleunigen? oder doch Motor auswechseln?) schreiben zu können?

    Xin schrieb:

    CStoll schrieb:

    Ja, Integers kann man ver-und-en - aber Integers verwendet man auch nicht als Basis für eigene Klassen, um diese verunden zu können.

    Warum eigentlich nicht?
    Grade das ist doch die Kritik derer, die das "reine OOP" in C++ suchen.

    Weil's keinen Sinn macht? Weil C++ keine reine OOP-Sprache ist? Such dir eine Erklärung aus.

    Xin schrieb:

    CStoll schrieb:

    Xin schrieb:

    CStoll schrieb:

    Daß sie dokumentiert sind, glaube ich dir, aber hast du die ganzen Fallstricke, die du auf den letzten Seiten erfolgreich ignoriert hast (ala "verwende niemals 'if(win)'" oder die möglichen Abhängigkeiten verschiedener Flags) dort ausführlich beschrieben? Wenn nein, wird ziemlich schnell jemand auf die Idee kommen, die "verbotenen" Möglichkeiten auszunutzen.

    Wer if( win.Flags ) benutzt wird sowieso gesteinigt, sofern das nicht explizit erlaubt und begründet ist. Sobald da eine Flage hinzugefügt wird, die unabhängig von den Flaggen ist, die damit abgefragt werden wollen, geht das ganze schief. if( win.Flags & KombinationAusGewünschtenFlaggen ) ist wohl Pflicht, selbst wenn das alle existierenden Flaggen sind und der Rest nicht verwendet wird und grundsätzlich auf 0 steht.

    if(win.Flags) kann ich explizit verbieten, indem ich den Member privat mache (und nur HasFlag() etc anbiete), dein if(win) kann nur unter Aufwand verboten werden.

    Du packst operator bool() als privates Member in Window?

    Ich definiere überhaupt keinen operator bool(), wenn ich es vermeiden kann - und wenn, dann so, daß er eine klare Bedeutung hat. Aber du mußt ihn privat definieren, um Konstrukte wie "if(win)..." zu verhindern.

    Xin schrieb:

    Das ist das Problem... ich brauche keine Regeln - ich brauche Begründungen, warum Regeln existieren. Viele Regeln existieren nämlich nur, weil man das schon immer so gemacht hat.
    Das glaube ich in dem Fall nicht, aber es beantwortet nicht die Frage, warum selbst unmögliche Fälle als Möglichkeit erkannt werden, um dann zu argumentieren, dass es mehrdeutig sei.

    Die Namensauflösung existiert nicht nur für Methoden, sondern für ALLE Namen. Und da ist es mir lieber, eine einheitliche Regel zu haben als ein dickes Paket an Ausnahmen (mal ist es erlaubt, wenn mehrere gleichnamige Gegenstände zusammenprallen, mal nicht).

    Sicherlich ist hier eine Gefahr, wenn unwissentlich, mehrere Funktionen gleichen Namens und unterschiedlicher Signatur zusammen kommen, dass man die Basisklassenzugehörigkeit vertauschen könnte. Hier fehlt mir die Möglichkeit, die Zusammenführung aus der Basisklasse heraus explizit zu erlauben, ohne dafür neuen Code zu produzieren.
    In meinem Fall ist die Zusammenführung ja gewünscht.

    Wenn dir so viel daran liegt, kannst du ja mal das ANSI-Kommitee darauf ansprechen - vielleicht nehmen sie deinen Vorschlag in die nächste Standardversion auf, wenn du ihn begründen kannst.



  • CStoll schrieb:

    Wenn dir so viel daran liegt, kannst du ja mal das ANSI-Kommitee darauf ansprechen - vielleicht nehmen sie deinen Vorschlag in die nächste Standardversion auf, wenn du ihn begründen kannst.

    Nicht eher ISO?



  • 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.

    Darf man fragen, warum?

    edit: Hab nur die ersten zwei Seiten gelesen, hoffe es wurde nicht schon beantwortet (die Antwort scheint ja sowieso glasklar zu sein..).



  • Unfug hier war.



  • CStoll schrieb:

    Xin schrieb:

    Flags in Verbindung mit operator = ist immer risikobehaftet.

    Und da muß man dann auch aufpassen, welche Teilmenge der Flags man nun tatsächlich erwischt.

    Das verstehe ich jetzt nicht!?

    CStoll schrieb:

    Die Klasse Flagable ist kein Flag, also soll sie sich auch nicht wie eins verhalten.

    Jow, dem stimme ich zu, allerdings muss man es auch nicht übertreiben. Ob die einzelnen Flags nun in einem Namespace WindowFlag oder mit WindowFlags zusammengefasst werden, spielt meines Erachtens nun keine so große Rolle.

    CStoll schrieb:

    Xin schrieb:

    Ich habe keine Fenster in meinem Framework, aber wenn Du mich so fragst, warum sollte ich Fenster nicht von einem Punkt ableiten? Genauer von einem Field, das ist eine positionierte(Point) Fläche (Area).

    Wenn du keine Fenster hast, warum reden wir die ganze Zeit über WindowFlags?

    Weil nicht mein Framework das Problem ist, sondern jemand die Frage gestellt, wie man mehrere Bool-Variablen verpacken kann. ^^

    CStoll schrieb:

    und bei der Beziehung Fenster->Punkt mußt du auch deutlich machen, wo das Fenster relativ zu seinen Punkt-Koordinaten liegt.

    Schon wieder verstehe ich Dich nicht!? Wo sollte es denn sonst liegen, wenn nicht da, wo es seine Positionspunkt hat?

    CStoll schrieb:

    Xin schrieb:

    Um ein Fenster zu verschieben setze man entweder den Punkt oder verschiebe es um eine Distance.

    Vermutlich würde ich allerdings von Bitmap(abgeleitet von einer BitmapDescription, welche von Area abgeleitet wurde) ableiten und Punkt ableiten und einen operator & Field() erstellen.

    win += Distance( 10, 10 );                  // Ein wenig verschieben.
    

    Und wer sagt mir, daß operator+= nicht die Fenstergröße korrigiert? Auf den ersten Blick mag das ja ganz nett sein, aber mir ist es trotzdem lieber zu wissen, worauf sich eine Zuweisung bezieht.

    Erstens sagt Dir das der Menschenverstand. Wenn Du ein Fenster um eine Distance verschiebst - und nichts anderes ist eine Addition, wenn Du Dir den Zahlenstrahl in der Grundschule in Erinnerung rufst - dass sich das Fenster dann verschiebt.
    Wenn Du das nicht intuitiv findest, hilft in der Regel ein Blick in die Klassenhirachie.

    CStoll schrieb:

    Um mal vom Fenster-Beispiel wegzukommen: Würdest du eine Klasse Auto von Farbe, Höchstgeschwindigkeit, Leistung oder aktueller Geschwindigkeit ableiten, nur um "a=rot;" (umlackieren) oder "a+=10;" (beschleunigen? oder doch Motor auswechseln?) schreiben zu können?

    Nein, in C++ nicht unbedingt.
    Ich würde ein Auto allerdings von Eigenschaften ableiten, die ich mehrfach verwende. So wäre es durchaus möglich, dass ich ein Auto von Farbe ableite, da ich bereits farbverarbeitende Klassen habe, die Farbe von anderen Teilen der Klasse differenzieren.
    Sprachlich sind wir da genauso flexibel, wie in der Programmierung.
    Ein (einfarbiges) Auto kann eine Farbe sein: "Die Farbe ist rot" => "Das Auto ist rot."
    Und natürlich kann ein Auto auch eine Farbe haben: "Die Farbe hat den Wert rot" => "Eine Komponente des Autos ist die rote Farbe."
    Sprachlich gehen wir damit also genauso flexibel um.

    Für Leistung und aktuelle Geschwindigkeit sind bei mir keine Klassen vorhanden. Ich wüßte derzeit auch keinen Grund, derartige Klassen zur Wiederverwertung abstrahieren. Das wären bei mir vermutlich Member.
    Grundsätzlich habe ich aber kein Problem damit, bei eindeutigen Aussagen Vererbung zu nutzen.

    leistung = Leistung( meinAuto );
    meinAuto += Geschwindigkeit( 5 );  // Beschleunigen;
    

    Vererbung ist prinzipiell nichts anderes als Membervariablen, Membervariablen entstanden aus der strukturierten Programmierung. Ich halte das Konzept der Membervariablen für sinnvoll, oft auch für richtig, aber in vielerlei Hinsicht ebenso für überholt, denn Vererbung kann Code vereinfachen.
    Member halte ich für sinnvoll, wenn sie untergeordnete Objekte enthält.
    Autos bestehen aus Sitzen, sie sind aber keine. Sie können eigenständige Eigenschaften besitzen, die in Gleichbehandlung mit dem Auto mehrdeutig sein könnten:

    langweiligesAuto.Sitze = Farbe( langweiligesAuto );
    

    Farbe wäre hier in einer Vererbungsbeziehung nicht eindeutig. Ein Auto als Sitzmöglichkeit anzusehen, würde ich für akzeptabel halten. Allerdings ist "Sitzmöglichkeit" keine Eigenschaft eines Autos, die unbedingt derart hervorgehoben werden muss. Aber auch das ist subjektiv - meine Oma sieht das anders.

    CStoll schrieb:

    Xin schrieb:

    CStoll schrieb:

    Ja, Integers kann man ver-und-en - aber Integers verwendet man auch nicht als Basis für eigene Klassen, um diese verunden zu können.

    Warum eigentlich nicht?
    Grade das ist doch die Kritik derer, die das "reine OOP" in C++ suchen.

    Weil's keinen Sinn macht? Weil C++ keine reine OOP-Sprache ist? Such dir eine Erklärung aus.

    Ob es keinen Sinn hat, wage ich zu bezweifeln. Die Frage gilt ja auch nicht nach dem Ist-Zustand von C++, sondern einem möglichen Sollte-Zustand.

    CStoll schrieb:

    Xin schrieb:

    CStoll schrieb:

    if(win.Flags) kann ich explizit verbieten, indem ich den Member privat mache (und nur HasFlag() etc anbiete), dein if(win) kann nur unter Aufwand verboten werden.

    Du packst operator bool() als privates Member in Window?

    Ich definiere überhaupt keinen operator bool(), wenn ich es vermeiden kann - und wenn, dann so, daß er eine klare Bedeutung hat. Aber du mußt ihn privat definieren, um Konstrukte wie "if(win)..." zu verhindern.

    Und? Wir müssen beide etwas als private deklarieren, um zu verhindern, dass ein Programmierer eine idotische Frage stellen kann. Es gibt diesbezüglich keinen Unterschied zwischen unseren Vorgehen.

    Und ganz ehrlich, es gibt eine Grenze, da sichere ich nicht mehr ab. Ich baue keinen Zaun um einen großen Gastank, nur weil auf dem Tank jemand grillen könnte.
    Darwin-Awards beweist, dass derartiges gemacht wird und das es tödliche Konsequenzen haben kann.
    Ich finde den Kommentar "Kann Nüsse enthalten, bei Nussallergie nicht konsumieren" auf einer Packung Erdnüsse für lächerlich.

    Wenn ein Programmierer nicht weiß, was es bedeutet alle Flags gleichzeitig abzufragen, obwohl noch unbelegte Flags vorhanden sind (oder könnten), dann darf er gerne auch auf einem Gastank grillen.
    Wenn's da knallt, dann sehe ich mich in beiden Fällen nicht in der Verantwortung, da reiche ich guten Gewissens geröstete Erdnüsse zu, ohne nach Allergien zu fragen.

    CStoll schrieb:

    Xin schrieb:

    Das ist das Problem... ich brauche keine Regeln - ich brauche Begründungen, warum Regeln existieren. Viele Regeln existieren nämlich nur, weil man das schon immer so gemacht hat.
    Das glaube ich in dem Fall nicht, aber es beantwortet nicht die Frage, warum selbst unmögliche Fälle als Möglichkeit erkannt werden, um dann zu argumentieren, dass es mehrdeutig sei.

    Die Namensauflösung existiert nicht nur für Methoden, sondern für ALLE Namen. Und da ist es mir lieber, eine einheitliche Regel zu haben als ein dickes Paket an Ausnahmen (mal ist es erlaubt, wenn mehrere gleichnamige Gegenstände zusammenprallen, mal nicht).

    Solange man auf die Mehrfachvererbung verzichtet, klappt alles wunderbar. Die Mehrfachausnahme sorgt für die Ausnahme.

    CStoll schrieb:

    Sicherlich ist hier eine Gefahr, wenn unwissentlich, mehrere Funktionen gleichen Namens und unterschiedlicher Signatur zusammen kommen, dass man die Basisklassenzugehörigkeit vertauschen könnte. Hier fehlt mir die Möglichkeit, die Zusammenführung aus der Basisklasse heraus explizit zu erlauben, ohne dafür neuen Code zu produzieren.
    In meinem Fall ist die Zusammenführung ja gewünscht.

    Wenn dir so viel daran liegt, kannst du ja mal das ANSI-Kommitee darauf ansprechen - vielleicht nehmen sie deinen Vorschlag in die nächste Standardversion auf, wenn du ihn begründen kannst.

    Für mich ist das Problem durch die Typerweiterung geklärt.
    Ich halte C++ für eine der besten Sprachen, die verfügbar sind - und dennoch ist es eine Sprache, die keine Zukunft haben darf, weil zuviele Probleme aus C geerbt wurden.

    Badestrand schrieb:

    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.

    Darf man fragen, warum?

    Es ist nicht falsch, es ist nur ungeschickt, weil ein Quadrat weniger Informationen benötigt als ein Rechteck. Es wäre aus klassenorientierter Sicht sinnvoller OOP zu verwendenden und ein RechteckInterface zu erstellen, wovon sich Quadrat und Rechteck abzuleiten.



  • Xin schrieb:

    Badestrand schrieb:

    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.

    Darf man fragen, warum?

    Es ist nicht falsch, es ist nur ungeschickt, weil ein Quadrat weniger Informationen benötigt als ein Rechteck. Es wäre aus klassenorientierter Sicht sinnvoller OOP zu verwendenden und ein RechteckInterface zu erstellen, wovon sich Quadrat und Rechteck abzuleiten.

    Oh, right, danke! Aber nebenbei: Hattet ihr (du und CStoll) nicht erst eine Riesen-Diskussion in einem anderen OOP-Thread? Immer diese ellenlangen Quote-Orgien, ihr kommt doch sowieso auf keinen Nenner 😕



  • Xin schrieb:

    CStoll schrieb:

    Xin schrieb:

    Flags in Verbindung mit operator = ist immer risikobehaftet.

    Und da muß man dann auch aufpassen, welche Teilmenge der Flags man nun tatsächlich erwischt.

    Das verstehe ich jetzt nicht!?

    Wir sind immer noch bei der alten Frage: Setze ich mit "wnd=Window::Modal;" nun das Fullscreen-Flag zurück oder nicht?

    Xin schrieb:

    CStoll schrieb:

    und bei der Beziehung Fenster->Punkt mußt du auch deutlich machen, wo das Fenster relativ zu seinen Punkt-Koordinaten liegt.

    Schon wieder verstehe ich Dich nicht!? Wo sollte es denn sonst liegen, wenn nicht da, wo es seine Positionspunkt hat?

    Der Punkt ist ziemlich klein im Vergleich zu einem Fenster - also welchen Wert erhalte ich, wenn ich ein Fenster als Punkt betrachte? die linke obere Ecke? die rechte untere Ecke? den Mittelpunkt? ...?
    (selbst wenn wir nur von Punkt->Rechteck ausgehen, gibt es mehr als genug Möglichkeiten zu sagen, was "meine" Position sein soll)

    Xin schrieb:

    CStoll schrieb:

    Xin schrieb:

    Um ein Fenster zu verschieben setze man entweder den Punkt oder verschiebe es um eine Distance.

    Vermutlich würde ich allerdings von Bitmap(abgeleitet von einer BitmapDescription, welche von Area abgeleitet wurde) ableiten und Punkt ableiten und einen operator & Field() erstellen.

    win += Distance( 10, 10 );                  // Ein wenig verschieben.
    

    Und wer sagt mir, daß operator+= nicht die Fenstergröße korrigiert? Auf den ersten Blick mag das ja ganz nett sein, aber mir ist es trotzdem lieber zu wissen, worauf sich eine Zuweisung bezieht.

    Erstens sagt Dir das der Menschenverstand. Wenn Du ein Fenster um eine Distance verschiebst - und nichts anderes ist eine Addition, wenn Du Dir den Zahlenstrahl in der Grundschule in Erinnerung rufst - dass sich das Fenster dann verschiebt.
    Wenn Du das nicht intuitiv findest, hilft in der Regel ein Blick in die Klassenhirachie.

    Ja, ich muß wieder nachsehen - und das notfalls über mehrere Dateien hinweg, bis ich den richtigen op+= für meinen Zweck finde.

    Xin schrieb:

    CStoll schrieb:

    Um mal vom Fenster-Beispiel wegzukommen: Würdest du eine Klasse Auto von Farbe, Höchstgeschwindigkeit, Leistung oder aktueller Geschwindigkeit ableiten, nur um "a=rot;" (umlackieren) oder "a+=10;" (beschleunigen? oder doch Motor auswechseln?) schreiben zu können?

    Nein, in C++ nicht unbedingt.
    Ich würde ein Auto allerdings von Eigenschaften ableiten, die ich mehrfach verwende. So wäre es durchaus möglich, dass ich ein Auto von Farbe ableite, da ich bereits farbverarbeitende Klassen habe, die Farbe von anderen Teilen der Klasse differenzieren.
    Sprachlich sind wir da genauso flexibel, wie in der Programmierung.
    Ein (einfarbiges) Auto kann eine Farbe sein: "Die Farbe ist rot" => "Das Auto ist rot."
    Und natürlich kann ein Auto auch eine Farbe haben: "Die Farbe hat den Wert rot" => "Eine Komponente des Autos ist die rote Farbe."
    Sprachlich gehen wir damit also genauso flexibel um.

    Ja, die Sprache kann manchmal recht mehrdeutig sein - und niemanden stört's 😃 Aber aus OOP-Sicht ist die Vererbung von Farbe->Auto trotzdem Käse. (und um ein Auto an eine farbverarbeitende Funktion zu übergeben, ist eine Typumwandlung über 'operator Farbe()' besser geeignet).

    Xin schrieb:

    Für Leistung und aktuelle Geschwindigkeit sind bei mir keine Klassen vorhanden. Ich wüßte derzeit auch keinen Grund, derartige Klassen zur Wiederverwertung abstrahieren. Das wären bei mir vermutlich Member.
    Grundsätzlich habe ich aber kein Problem damit, bei eindeutigen Aussagen Vererbung zu nutzen.

    leistung = Leistung( meinAuto );
    meinAuto += Geschwindigkeit( 5 );  // Beschleunigen;
    

    Vererbung ist prinzipiell nichts anderes als Membervariablen, Membervariablen entstanden aus der strukturierten Programmierung. Ich halte das Konzept der Membervariablen für sinnvoll, oft auch für richtig, aber in vielerlei Hinsicht ebenso für überholt, denn Vererbung kann Code vereinfachen.

    Vererbung ist keine verkappte Member-Beziehung - mit der Vererbung gehst du auch einen Vertrag ein, das Interface der Basisklasse konsistent umzusetzen. (und außerdem hat ein Auto zwei Geschwindigkeiten - die aktuelle und die maximale - wie willst du das per Vererbung darstellen?

    Xin schrieb:

    Member halte ich für sinnvoll, wenn sie untergeordnete Objekte enthält.
    Autos bestehen aus Sitzen, sie sind aber keine. Sie können eigenständige Eigenschaften besitzen, die in Gleichbehandlung mit dem Auto mehrdeutig sein könnten:

    langweiligesAuto.Sitze = Farbe( langweiligesAuto );
    

    Farbe wäre hier in einer Vererbungsbeziehung nicht eindeutig. Ein Auto als Sitzmöglichkeit anzusehen, würde ich für akzeptabel halten. Allerdings ist "Sitzmöglichkeit" keine Eigenschaft eines Autos, die unbedingt derart hervorgehoben werden muss. Aber auch das ist subjektiv - meine Oma sieht das anders.

    Wie du inzwischen bemerkt hast, nicht nur die 😃

    Xin schrieb:

    CStoll schrieb:

    Xin schrieb:

    CStoll schrieb:

    Ja, Integers kann man ver-und-en - aber Integers verwendet man auch nicht als Basis für eigene Klassen, um diese verunden zu können.

    Warum eigentlich nicht?
    Grade das ist doch die Kritik derer, die das "reine OOP" in C++ suchen.

    Weil's keinen Sinn macht? Weil C++ keine reine OOP-Sprache ist? Such dir eine Erklärung aus.

    Ob es keinen Sinn hat, wage ich zu bezweifeln. Die Frage gilt ja auch nicht nach dem Ist-Zustand von C++, sondern einem möglichen Sollte-Zustand.

    Dann gib mir doch mal ein sinnvolles Beispiel, um von int ableiten zu müssen.

    (und zur Not kannt du auch eine echte OOP-Sprache verwenden, wie z.B. Java (Oops- da kann man ja auch nicht von Integer ableiten 😮 ))

    Xin schrieb:

    CStoll schrieb:

    Xin schrieb:

    CStoll schrieb:

    if(win.Flags) kann ich explizit verbieten, indem ich den Member privat mache (und nur HasFlag() etc anbiete), dein if(win) kann nur unter Aufwand verboten werden.

    Du packst operator bool() als privates Member in Window?

    Ich definiere überhaupt keinen operator bool(), wenn ich es vermeiden kann - und wenn, dann so, daß er eine klare Bedeutung hat. Aber du mußt ihn privat definieren, um Konstrukte wie "if(win)..." zu verhindern.

    Und? Wir müssen beide etwas als private deklarieren, um zu verhindern, dass ein Programmierer eine idotische Frage stellen kann. Es gibt diesbezüglich keinen Unterschied zwischen unseren Vorgehen.

    Der Unterschied ist konzeptionell: Ich deklariere die Flag von vornherein privat, weil ihre Existenz und Aufbau nichts im offiziellen Interface zu suchen haben. Du mußt deinen Umwandlungsoperator privat nachträglich privat deklarieren, um unsinnige Verwendungen zu verhindern.

    Und damit sind wir doch wieder bei der Rechteck-Quadrat-Problematik angekommen: Du sagst: "ein Fenster ist ein Flag", "ein Flag kann nach bool konvertiert werden" und "ein Fenster kann nicht nach bool konvertiert werden" - siehst du den Widerspruch in diesen Aussagen?

    Xin schrieb:

    Und ganz ehrlich, es gibt eine Grenze, da sichere ich nicht mehr ab. Ich baue keinen Zaun um einen großen Gastank, nur weil auf dem Tank jemand grillen könnte.
    Darwin-Awards beweist, dass derartiges gemacht wird und das es tödliche Konsequenzen haben kann.
    Ich finde den Kommentar "Kann Nüsse enthalten, bei Nussallergie nicht konsumieren" auf einer Packung Erdnüsse für lächerlich.

    Du bist ja auch kein Allergiker.

    Xin schrieb:

    CStoll schrieb:

    Xin schrieb:

    Das ist das Problem... ich brauche keine Regeln - ich brauche Begründungen, warum Regeln existieren. Viele Regeln existieren nämlich nur, weil man das schon immer so gemacht hat.
    Das glaube ich in dem Fall nicht, aber es beantwortet nicht die Frage, warum selbst unmögliche Fälle als Möglichkeit erkannt werden, um dann zu argumentieren, dass es mehrdeutig sei.

    Die Namensauflösung existiert nicht nur für Methoden, sondern für ALLE Namen. Und da ist es mir lieber, eine einheitliche Regel zu haben als ein dickes Paket an Ausnahmen (mal ist es erlaubt, wenn mehrere gleichnamige Gegenstände zusammenprallen, mal nicht).

    Solange man auf die Mehrfachvererbung verzichtet, klappt alles wunderbar. Die Mehrfachausnahme sorgt für die Ausnahme.

    Dann vergleiche mal die Fälle, wo Namen zusammenprallen legal könnten, mit denen, wo es ein Fehler ist. Das dürfte eine relativ lange Liste von Fallunterscheidungen zwischen "erlaubt" (z.B. zwei Funktionen mit unterschiedlicher Signatur), "nicht erlaubt" (z.B. zwei Member) und "gelegentlich erlaubt" (z.B. Funktion vs. Member) ergeben - viel Spaß dabei, alle Fälle herauszufischen und zu implementieren.

    Xin schrieb:

    CStoll schrieb:

    Sicherlich ist hier eine Gefahr, wenn unwissentlich, mehrere Funktionen gleichen Namens und unterschiedlicher Signatur zusammen kommen, dass man die Basisklassenzugehörigkeit vertauschen könnte. Hier fehlt mir die Möglichkeit, die Zusammenführung aus der Basisklasse heraus explizit zu erlauben, ohne dafür neuen Code zu produzieren.
    In meinem Fall ist die Zusammenführung ja gewünscht.

    Wenn dir so viel daran liegt, kannst du ja mal das ANSI-Kommitee darauf ansprechen - vielleicht nehmen sie deinen Vorschlag in die nächste Standardversion auf, wenn du ihn begründen kannst.

    Für mich ist das Problem durch die Typerweiterung geklärt.
    Ich halte C++ für eine der besten Sprachen, die verfügbar sind - und dennoch ist es eine Sprache, die keine Zukunft haben darf, weil zuviele Probleme aus C geerbt wurden.

    Ja, die Typerweiterung ist gut - aber wie bereits geklärt wurde, kannst du die nicht effektiv mit Vererbung nachbilden. (und Sprachmittel 1:1 zwischen verschiedenen Sprachen umrechnen zu wollen ist sowieso unmöglich)

    Badestrand schrieb:

    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.

    Darf man fragen, warum?

    Bei einer Vererbung darf man (aus OOP-Sicht) die Eigenschaften der Basisklasse nicht einschränken, sondern nur erweitern - Rechteck hat eine SetWidth() und SetHeight(), die jeweils eine Ausdehnung ändern können, Quadrat sichert zu, daß beide Seiten gleich lang sind - d.h. egal wie du SetWidth() implementierst, zerstörst du damit entweder die Invarianten des Rechtecks oder des Quadrats:

    void resize(rectangle& r)
    {
      r.SetWidth(10);
      r.SetHeight(5);
      ASSERT(r.GetWidth()==10 && r.GetHeight()==5);//entweder dieser Assert fliegt dir um die Ohren
    }
    
    int main()
    {
      quadrat q;
      resize(q);
      ASSERT(q.GetWidth()==q.GetHeight());//oder dieser
    }
    


  • car+=10;
    

    oder

    win += rect;
    

    Das ist doch mal eindeutig was es macht 🙂

    Ich habe lange Zeit nicht verstanden warum Leute gegen Operatoren-Überladung sein können - nun weiß ich es...


Anmelden zum Antworten