"to MI or not to MI"



  • Fakt ist, dass ein Klasse, die eine Einschränkung ihrer Basis hat, natürlich nicht public erben darf.

    Vererbung hat erstmal nichts mit Zugriffsmodifier zu tun. C++ kann auch ohne public/private funktionieren, genauso kann auch const entfernt werden, ohne das etwas kaputt geht.

    So siehts aus: Allgemeines Konzept -> Umsetzung in einer Sprache. Aber was du machst: Eine Umsetzung in C++ erlaubt deine Idee und du erhebst sie zum allgemeinen Konzept. Leider "funktioniert" es nur unter C++. Doch nicht mehr so allgemein. Ausserdem sind schon sehr viele Probleme angesprochen worden, die vielleicht geloest werden koennen, aber im Vergleich zum traditionellen Vererbungsbegriff eher unelegant wirken.

    Konkret: Kreise erben von Zahlen, Zahlen koennen in unmissverstaendlicher Weise addiert werden und ergeben wieder eine Zahl. Was ist die Addition von zwei Kreisen und ist es wieder ein Kreis? Da gibt es viele Interpretationen.

    Wer scheiße programmiert, wird scheiße erhalten.

    Genau darum geht es. Dein Konzept ist scheisse.



  • Geht es im Kern darum, ob ein Enkel von Eltern und Tanten erben darf
    und einzig die Prämisse im Raum steht
    ob er/sie das Radfahren jetzt vom Onkel oder Vater lernt ?

    Wenn man s rein logisch angeht
    wäre der erste "Lehrer" wohl der Gewinner..



  • Erbt nun bei Deinem Entwurf Wurstbrot von Supermatrkt oder Supermarkt von Wurstbrot?

    Ist ja irgendwie beides richtig.

    class Wurstbrot:Supermarkt {
       //getVerkaufer wird ohne sinnlosen Schreibaufwand geerbt
    }
    

    Aber auch

    class Supermarkt:Wurstbrot {
       //getVerkaufer wird ohne sinnlosen Schreibaufwand geerbt
    }
    

    Hmm.



  • volkard schrieb:

    Erbt nun bei Deinem Entwurf Wurstbrot von Supermatrkt oder Supermarkt von Wurstbrot?

    Das ist der Punkt ^^

    Sowohl das Klima im Supermarkt ändert dein Brot,
    genauso wie dein Brot zum Klima im Supermarkt beiträgt ^^

    Edit:
    Ich denke spätestens wenn Quantencomputer auf den Consumer-Markt drängen,
    wird sich da mit der Vererbung nochmal kräftig was tun.. 🙂
    Alles nur Humbug ?
    Guckst Du hier http://www.dwavesys.com/en/dw_homepage.html
    Abnehmer - Lockheed Martin ..



  • volkard schrieb:

    Nur die Postleitzahl 0 ist nicht valide, alle anderen Postleitzahlen sind in Ordnung. Denn wir haben den Operator ! von Integer geerbt.

    Nein. Nur ein Teil der PLZ sind valide, z.B. ist 00001 keine valide PLZ. Tatsächlich sind ein Großteil der möglichen PLZ nicht in Gebrauch und damit nicht valide. Abgesehen davon gibt es in anderen Ländern "Postcodes" die nicht nur auf Ziffern basieren.

    volkard schrieb:

    Ist es überhaupt wahr, zu sagen "Die Postleitzahl IST eine Zahl?"

    Nein. Darum schrieb ich ja, dass es keinen Sinn macht, PLZ von Integer erben zu lassen.



  • Shade Of Mine schrieb:

    Ich bin noch immer verwirrt.
    Wie genau kann nun Kreis feststellen wann sich sein Radius geaendert hat?

    Das bezog sich nicht auf diese Frage. Bau Dir Events ein, mach, was immer Du unter C++ auch gemacht hast.

    Da ändert sich nix.

    Shade Of Mine schrieb:

    Wenn C++ hier ungeeignet ist, koennen wir auch gerne in Scala oder Ruby modellieren.

    Wir können das gerne, wobei ich das nur gerne könnte. 😉 Meine Scala-Kenntnisse sind überschaubar (ich arbeite dran, wenn ich mal endlich mal wieder Zeit dafür habe) und meine Rubykenntnisse noch viel überschaubarer.

    Das Ganze dreht sich ja auch nicht darum, das hier aufzublähen, sondern die Gleichwertigkeit zu demonstrieren. Dafür habe ich eben einen C++-Code mit reichlich Boilerplate reingepackt, in den kannst Du beim Zuweisungsoperator von Radius reinpacken, was immer Du magst. Beispielsweise jemanden informieren, dass sich der Radius geändert hat.

    Bitte erzähl mir jetzt nicht, dass ich gefälligst <BeliebigeSpracheEinsetzen> lernen soll, damit ich mir angucken kann, wie man das richtig macht. Ich schaue mir regelmäßig neue Sprachen an, wenn ich Zeit dafür habe und jeder hat da seine persönliche Meinung, welche Sprache die richtige ist.

    volkard schrieb:

    Abgesehen von viel Code um nichts, machste wieder den einen Generalfehler, daß Du nicht programmierst, um folgende Flüchtigkeitsfehler aufzudecken.

    Viel Code um nichts. Richtig. Boilerplate. Aber zu inlinen, generierbar (sowas machen Compiler), bzw. nur in C++ erforderlich, um Anweisungen zu forwarden: wenn der Compiler diese Forwards automatisch macht, indem er zum Beispiel direkt die zu rufende Funktion einsetzt, wird da auch tatsächlich wieder "nichts" draus. Nur mit C++ ist das eben nicht formulierbar.

    Du hast vor meinem Posting einen anderes Posting gesetzt, dass ich jetzt erst gesehen habe:

    volkard schrieb:

    Was Xin braucht, ist wohl C++:

    class Postleitzahl:mixin Integer{
       promote relational;//<, <=, ==, >=, >, !=
       promote std::hash(Integer);//sind ja gute Hashtable-Schlüssel.
       promote constructors, copy, assign;//Jo, die sind ok
       friend ostream& operator<<(ostream& out,Postleitzahl plz);//to be implemented
    }
    

    Das ist in der Granularität durchaus gewünscht. Nur müssen die Standardfälle in Kurzform definiert werden können.

    volkard schrieb:

    Radius & operator = ( int radius )
    { Integer::Value = radius < 0 ? 0 : radius; }
    

    verdeckt Fehler und macht sie nur schwerer findbar.

    Sorry, wenn ich um die Uhrzeit nicht mehr alles so optimal hinbekomme.

    Radius & operator = ( Radius radius )
    { Integer::Value = radius.Integer::Value < 0 ? 0 : radius.Integer::Value; }
    

    Führt zu

    Streckenlänge s( 10 );
    Radius r(5);
    
    r = Radius( s );
    

    knivil schrieb:

    Eine Umsetzung in C++ erlaubt deine Idee und du erhebst sie zum allgemeinen Konzept. Leider "funktioniert" es nur unter C++. Doch nicht mehr so allgemein. Ausserdem sind schon sehr viele Probleme angesprochen worden, die vielleicht geloest koennen, aber im Vergleich zum traditionellen Vererbungsbegriff eher unelegant wirken.

    In C++ sind wir hier an einer Stelle, die in C++ ziemlich unelegant ist. Beim SubtypeableOperator hingegen empfinde ich das auch in C++ elegant und angemessen. Nur in solchen Fällen nutze ich Mehrfachvererbung in C++.

    knivil schrieb:

    Wer scheiße programmiert, wird scheiße erhalten.

    Genau darum geht es. Dein Konzept ist scheisse.

    Für C++ sind wir an dieser in einem Bereich, den niemand als "schön" verkaufen will.

    Alle bisher genannten Probleme sind mir bekannt und Lösungen sind in C++ nur teilweise überhaupt zu formulieren und wenn dann sieht das auch durchaus "scheiße" aus. Aber teilweise geht es in C++ und das ist zusammen zu führen, so dass nicht mehr zwischen Membern und Ableitung zu unterscheiden ist, sondern es nur noch eine Form Komposition gibt, die aber pro Element beschreibt, wie damit umgegangen werden soll. Das ganze hier ist ja nicht als Programmierempfehlung für C++ zu verstehen, sondern nur als Richtungszeiger, wohin man denken kann.

    Ihr habt euch hier auf die gleichen Probleme gestürzt wie ich. Ihr mit dem Ziel, mir zu sagen, dass "mein" Konzept scheiße ist und ich mache das mit dem Ziel das Standardproblem umreißen zu können. Und für Standardprobleme muss eine Sprache dann eine Standardlösung anbieten. Wir arbeiten in die gleiche Richtung nur mit unterschiedlicher Motivation: Ihr sagt mir viele Probleme, ich versuche eine Abstraktion dafür zu finden und eine Lösung zu erarbeiten.
    Ich sehe die Chance, dass das klappt, ich sehe da Potential, also probiere ich es aus.

    Zurück zur ursprünglichen Debatte: Beim SubtypeableOperator ist die Mehrfachvererbung, so wie sie in C++ formulierbar ist, hilfreich. Zum einen deswegen halte ich es für sinnvoll, sich mit Mehrfachvererbung auseinander zu setzen und zum anderen halte ich es für sinnvoll, um sich mit dem Konzept der Mehrfachvererbung vertraut zu machen, weil ich davon ausgehe, dass andere Sprachentwickler sich auch Gedanken zu dem Thema machen werden, weil Interfaces eigentlich auch nur ein "scheiß Konzept" sind und sich andere Entwickler auch um Lösungen bemühen.

    Java baut zurück und nennt das Interfaces. Scala wirft Traits in die Arena. Es ist halt viel Ausprobieren und wo am Ende etwas interessantes rauskommt, kann man auch nicht in so einem Thread behandeln, sondern braucht entsprechende Software, um das im Verlauf der Entwicklung dieser Software zu beobachten, was sich wirklich als praktisch erweist, wo etwas nur im Beispiel praktisch aussieht, aber wieder entfernt werden sollte oder wo sich vielleicht Möglichkeiten ergeben, die weiter aufgebaut werden können.

    Trotzdem ist dieser Thread für mich hilfreich, wenn man vielleicht mal von volkard, pVoid und ihrem Wurstbrot absieht, denn auch wenn ich noch nichts gelesen habe, was mir neu wäre, so zwingt es mich, die Sachen in Frage zu stellen und damit zu verifizieren.
    Bisher denke ich weiterhin, dass es ein guter Versuch ist. Und wenn nicht, bleibt trotzdem Subtypable und Operator als Grund für Mehrfachvererbung.



  • Xin schrieb:

    volkard schrieb:

    Radius & operator = ( int radius )
    { Integer::Value = radius < 0 ? 0 : radius; }
    

    verdeckt Fehler und macht sie nur schwerer findbar.

    Sorry, wenn ich um die Uhrzeit nicht mehr alles so optimal hinbekomme.

    Radius & operator = ( Radius radius )
    { Integer::Value = radius.Integer::Value < 0 ? 0 : radius.Integer::Value; }
    

    Führt zu

    Streckenlänge s( 10 );
    Radius r(5);
    
    r = Radius( s );
    

    Facepalm.
    Das Überdecken den Programmierfehlers r-=1000, Du Nase. Das "< 0 ? 0" ist ärgeranziehend.

    Ganz so, wie übrigens Deine falsch angewendete Vererbung auch. Eines der Hauptprobleme scheinz zu sein, daß Du überhaupt kein Gefühl für defensives Programmieren zu haben scheinst. Nur Tipparbeit sparen zu wollen, ist einfach kein guter Ratgeber.
    Schade, daß Du aufs umdrehen-Beispiel nicht eingehen magst.



  • Xin schrieb:

    Shade Of Mine schrieb:

    Ich bin noch immer verwirrt.
    Wie genau kann nun Kreis feststellen wann sich sein Radius geaendert hat?

    Das bezog sich nicht auf diese Frage. Bau Dir Events ein, mach, was immer Du unter C++ auch gemacht hast.

    Da ändert sich nix.

    Aber OK, du würdest hier also ein Notification System einbauen. Das kann man ja wiederum erben.
    Ich habe das ganze dann mal eben geschrieben:
    http://ideone.com/hHs59f

    Ist das so in etwa das was du dir vorstellst?
    C++ wehrt sich da irgendwie mit Händen und Füßen dagegen...

    Gibt es eine Sprache wo das etwas besser geht, weil - ohne dass du das jetzt bitte falsch verstehst - in C++ ist das nicht möglich so zu programmieren. Ich muss konstant Code verdoppeln und muss höllisch auf die Scopes aufpassen weil da dauernd irgendein Parent reinpfuscht.

    Mir ist egal welche Sprache. Such dir eine aus. Hauptsache es gibt Tutorials zum einlesen - dann gib mir ein paar Tage und wir können mit der Sprache weiter diskutieren.

    Falls wir bei C++ bleiben, dann habe ich gleich eine Frage:
    Kann man den op= irgendwie besser aufrufen als über die Qualifizierung des Ursprungstyps. ein
    k.Radius::operator=() ging leider nicht.

    uU habe ich ja auch was falsch gemacht - deshalb, wenn möglich - kannst du meinen Code ausbessern?

    Das Ganze dreht sich ja auch nicht darum, das hier aufzublähen, sondern die Gleichwertigkeit zu demonstrieren. Dafür habe ich eben einen C++-Code mit reichlich Boilerplate reingepackt, in den kannst Du beim Zuweisungsoperator von Radius reinpacken, was immer Du magst. Beispielsweise jemanden informieren, dass sich der Radius geändert hat.

    Boilerplate ist mir egal. Boilerplate ist nur Syntax. Wichtig ist, dass wir korrekt modellieren können.



  • volkard schrieb:

    Facepalm.
    Das Überdecken den Programmierfehlers r-=1000, Du Nase. Das "< 0 ? 0" ist ärgeranziehend.

    Ich baue hier kein perfektes Framework für geometrische Objekte auf. Das hier sind kurzgehaltende Beispiele, um eine Richtung anzudeuten, keine Programme, die Du danach als ein CAD kompilieren kannst. Es geht um Unterscheidungen - mir jedenfalls - nicht darum in einem speziellen Fall die perfekte Implementierung für eine Unterscheidung hier abzuliefern.
    Irgendwann muss so ein Posting auch mal fertig werden und falls dann etwas Unwichtiges nur irgendwie kompilierfähig gemacht wird, dann kannst Du Dir das sicherlich auch selbst kurz in Hübsch vorstellen.

    volkard schrieb:

    Ganz so, wie übrigens Deine falsch angewendete Vererbung auch. Eines der Hauptprobleme scheinz zu sein, daß Du überhaupt kein Gefühl für defensives Programmieren zu haben scheinst. Nur Tipparbeit sparen zu wollen, ist einfach kein guter Ratgeber.
    Schade, daß Du aufs umdrehen-Beispiel nicht eingehen magst.

    Weil es nichts mit Mehrfachvererbung zu tun hat, weil darauf bereits eingegangen wurde, weil der Fehler problemlos zu debuggen ist, weil die Hierarchie semantisch verifizierbar ist, wenn die Sprache das unterstützt, weil der Funktionsname schon scheiße gewählt ist, weil das Beispiel Schwachsinn ist, denn wer erlaubt, dass sich ein Kopf vier mal um 90 Grad umdrehen lässt, der tötet einen Menschen weil er eine Restriktion im Hals falsch implementiert (Hallo defensives Programmieren!) hat und nicht weil er vergessen hat den Menschen am richtigen Punkt umzudrehen, weil Du Fehler allgemein nicht grundsätzlich abfangen kannst und Du Deine Software erstmal mit einem Dummy testen musst, bevor Du einen Menschen damit fernsteuerst und das ganz normaler Entwicklungsalltag ist und Du den Fehler mit Membern genauso beschreiben kannst.

    Die Tatsache, dass man auch fehlerhafte Programme formulieren kann, weil man etwas anderes wünscht, als man geschrieben hat ist als Argument einfach Schwachsinn.

    Darum testet man Software im Idealfall und bringt dann erst Menschen damit um. (Sarkasmus-Schild-Hochhaltend)

    @Shade: Dafür das mit Dir durchzugehen, möchte ich mir mehr Zeit nehmen, damit ich den Code auch mal durch den Compiler jagen kann.

    Ich kann Dir nicht sagen, ob es dafür eine Sprache gibt, mir ist keine bekannt, sonst würde ich die ja verwenden und nicht selbst eine schreiben. ^^
    Kann ich die Probleme sprachlich einkreisen, kann ich sie auch (hoffentlich) sprachlich lösen. Wenn nicht oder sich das Ganze im praktischen Einsatz nicht als gut bewährt... dann Arschkarte. 😉

    Ich kann Dir allerdings nicht sagen, wann ich Ruhe dafür habe und die Zeit, Probleme in C++ zu formulieren, die ich so üblicherweise auch nicht in C++ formulieren würde.
    Ich habe jetzt keine Zeit dafür und ehrlich gesagt auch keinen Plan, ob oder wann ich diese Woche überhaupt noch Zeit für irgendwas habe. Sehr wahrscheinlich eben nicht, um mich in Ruhe und idealerweise auch mal ausgeschlafen an irgendeinen Compiler zu setzen.

    Shade Of Mine schrieb:

    Boilerplate ist mir egal. Boilerplate ist nur Syntax. Wichtig ist, dass wir korrekt modellieren können.

    Gute Basis. 🙂

    PS: Verdammt... bin zu neugierig... ich habe nochmal über den Code geguckt und fand das unten abgedruckte Ergebnis eigentlich doch so entsprechend meiner Erwartung.
    Entsprechend hast Du C++ offenbar ja doch gebändigt bekommen, obwohl es sich gewehrt hat. 😉

    Shade Of Mine schrieb:

    Falls wir bei C++ bleiben, dann habe ich gleich eine Frage:
    Kann man den op= irgendwie besser aufrufen als über die Qualifizierung des Ursprungstyps. ein
    k.Radius::operator=() ging leider nicht.

    Warum nicht?

    Hier werden vermutlich zwei Anpassungen gleichzeitig nötig sein, weswegen C++ streikt. Ich würde Radius::operator 😞 Radius const & r ) definieren und weiterleiten.

    Es reicht vermutlich reicht aber auch

    k.Radius::operator=( Radius( 7 ) );
    

    (Nicht ausprobiert, just an educated guess)



  • Xin schrieb:

    Weil es nichts mit Mehrfachvererbung zu tun hat,

    Irgendwie schon. Weil Vererbung bereits falsch ist, ist Mehrfachvererbung mehrfach falsch.



  • Ich scheitere gerade an der Funktion:

    void verdoppleRadius(Radius& r) {
        //was ich gerne hätte
        //r=r+r; oder r*=2; oder dergleichen
        //geht aber leider nicht :(
        r.ChangeObservableArithmeticType::operator=(r.asPositiveInteger()+r.asPositiveInteger());
    }
    

    Das Problem ist, dass PositiveInteger protected von Integer erbt und mein "operator Integer()" scheinbar vom Compiler ignoriert wird.

    Ich will jetzt operator+ für Integer verwenden um 2 PositiveInteger miteinander zu addieren. Aber das mag der Compiler nicht, da Integer eine inaccessible Base von PositiveInteger ist.

    Wenn ich

    r.ChangeObservableArithmeticType::operator=(r.asPositiveInteger().operator ::Integer()+r.asPositiveInteger().operator ::Integer());
    

    verwende - dann mag er nicht weil der op= einen ChangeObservableArithmeticType<PositiveInteger> erwartet aber einen Integer bekommt. Und er kann nicht in einem Schritt aus Integer einen ChangeObservableArithmeticType<PositiveInteger> machen...

    Ich müsste also eigentlich public von Integer erben. Aber dann kann ich keine Constraints für den Typ definieren. Jedenfalls hänge ich an der Stelle. Vielleicht fällt mir später ja noch eine Lösung ein...

    PS:
    Wenn ich in Radius einen Ctor definiere:

    Radius(ChangeObservableArithmeticType::BaseType value) : ChangeObservableArithmeticType(value) {}
    

    Dann kann ich in der Tat
    k.Radius::operator=(Radius(7));
    schreiben.

    PPS:
    eigentlich wollte ich Code verdopplung vermeiden... Oder meinst du es ist notwendig alle Operatoren mehrfach zu definieren? Das würde mein PositiveInteger/Integer Problem lösen indem ich alle Operatoren die Integer hat einfach für PositiveInteger neu definiere... Andererseits ist das ne Menge arbeit das Synchron zu halten.



  • Shade Of Mine schrieb:

    Ich scheitere gerade an der Funktion:

    void verdoppleRadius(Radius& r) {
        //was ich gerne hätte
        //r=r+r; oder r*=2; oder dergleichen
        //geht aber leider nicht :(
        r.ChangeObservableArithmeticType::operator=(r.asPositiveInteger()+r.asPositiveInteger());
    }
    

    Das Problem ist, dass PositiveInteger protected von Integer erbt und mein "operator Integer()" scheinbar vom Compiler ignoriert wird.

    Ich will jetzt operator+ für Integer verwenden um 2 PositiveInteger miteinander zu addieren. Aber das mag der Compiler nicht, da Integer eine inaccessible Base von PositiveInteger ist.

    Yepp, deswegen hatte ich in meinem Kreisgedöhns eine Funktion "Integer const & AsInteger() const", die die gleiche Funktion bietet.

    Shade Of Mine schrieb:

    Wenn ich

    r.ChangeObservableArithmeticType::operator=(r.asPositiveInteger().operator ::Integer()+r.asPositiveInteger().operator ::Integer());
    

    verwende - dann mag er nicht weil der op= einen ChangeObservableArithmeticType<PositiveInteger> erwartet aber einen Integer bekommt. Und er kann nicht in einem Schritt aus Integer einen ChangeObservableArithmeticType<PositiveInteger> machen...

    In C++ musst Du die Konvertierungen schon klarstellen, da Templates hier sehr restriktiv sind und auf die Vererbung oder Konvertierungsmöglichkeiten der Template-Parameter nicht eingehen kann (und auch nicht implizit sollte, aber eben auch keine Möglichkeit gibt, es implizit zu erlauben).

    Hier müsste das Verwandschaftsverhältnis von der Sprache erkannt oder wenigstens erlaubt werden (bzw. in C++ nochmals beschrieben werden).

    Shade Of Mine schrieb:

    PS:
    Wenn ich in Radius einen Ctor definiere:

    Radius(ChangeObservableArithmeticType::BaseType value) : ChangeObservableArithmeticType(value) {}
    

    Dann kann ich in der Tat
    k.Radius::operator=(Radius(7));
    schreiben.

    Hallo "Curiously Recurring Template Pattern"?



  • Ha, so jetzt funktioniert es besser:
    http://ideone.com/Wk2LNB

    Ich kann jetzt

    void verdoppleRadius(Radius& r) {
        r=r+r;
    }
    

    schreiben.

    PS:
    Diese asFoo() funktion mag ich nicht, weil die nicht implizit aufgerufen wird. Wenn ich
    r.asPositiveInteger().asInteger() aufrufen muss, komm ich mir irgendwie doof vor 😕

    Habe das Problem aber über Forward Makros gelöst.



  • Shade Of Mine schrieb:

    Ha, so jetzt funktioniert es besser:
    http://ideone.com/Wk2LNB

    Ich kann jetzt

    void verdoppleRadius(Radius& r) {
        r=r+r;
    }
    

    schreiben.

    Und wenn man sich den ganzen Quellcode anschaut, der dafür in C++ nötig ist, versteht man sehr gut, warum kein normaler Mensch sich auch nur Gedanken darüber macht, ob man darauf aufbauend vielleicht etwas Praktisches erreichen kann.
    Ja, schon klar, ich kann mir die Schlussfolgerung schon selbst denken... :->

    Shade Of Mine schrieb:

    PS:
    Diese asFoo() funktion mag ich nicht, weil die nicht implizit aufgerufen wird. Wenn ich
    r.asPositiveInteger().asInteger() aufrufen muss, komm ich mir irgendwie doof vor 😕

    Protected wird hier global über alles gelegt. Eigentlich müsste Integer unterscheiden, ob es als Objekt existiert und Zuweisungen von Außen zulässt oder ob es als Basisklasse für PositiveInteger() existiert. Da PositiveInteger() eigentlich eine Einschränkung der Menge gültiger Werte von Integer ist, ist nur der Teil von Operationen vererbbar, der einen Wert aus der gültigen Menge (den positiven Zahlen) nicht in einen ungültigen Wert verwandelt.

    Man kann also alle const-Methoden direkt vererben (was man in C++ über operator Integer CONST &() impliziet ausdrücken könnte, wenn C++ es erlauben würde), den Zuweisungsoperator hingegen muss man in PositiveInteger reimplementieren.

    Die operation - wäre also durchaus legitim: PositiveInteger( 1000 ) - PositiveInteger( 2000 ) ergibt ein Integer( -1000 ). Das wiederum könnte man über PositiveInteger::operator=( Integer const & ) zuweisen und hier prüfen, ob man diese Zuweisung das Objekt nicht in eine fehlerhaften Zustand überführt. Integer::operator=( Integer const & ) ist ja für PositiveInteger nicht sichtbar.

    Die Ableitungsformel wäre also quasi "const public mutable protected".



  • Ne, ich will das jetzt austesten - wenn es umständlich ist, dann ist es das halt. Das stört mich nicht.

    Ich habe gerade aufgegeben PunktXY observable zu machen, weil das einfach zu kompliziert geworden wäre. Ich habe mit Radius ja schon ein observable Konstrukt.

    http://ideone.com/5YY8an

    Das Problem dass ich damit habe ist, dass jedes Implementationsdetail öffentlich ist. Es ist zB nicht möglich beim Ursprung des Kreises plötzlich auf double zu wechseln wenn ich vorher int verwendet habe. Oder zB auf __int64 statt __int32 oder sowas. Weil überall in meinem Clientcode eben direkt PunktXY verwendet wird. Und PunktXY kann ich ja nicht ändern, weil das von allen anderen Klassen auch verwendet wird.

    Was cool ist, ist printKoordinaten. Das ist richtig cool. Aber das ist jetzt die einzige Situation wo ich was cooles gefunden habe.

    Ich bin mir auch nicht ganz sicher über solche Sachen wie Radius=Laenge*PunktX/PunktY.

    Mein Fazit: in C++ komme ich nichtmal zu der Stelle wo es interessant wird, weil die Sprache sich mit Händen und Füßen gegen diesen Ansatz wehrt.

    Gibt es eine Sprache in der dein Ansatz verfolgbar ist? Du musst ja schon Erfahrung damit haben: hast du vielleicht Code wo du das Umgesetzt hast? Wie gesagt - Sprache ist mir egal. Aber in C++ kommen wir hier auf keinen Grünen Zweig.



  • Shade Of Mine schrieb:

    Ne, ich will das jetzt austesten - wenn es umständlich ist, dann ist es das halt. Das stört mich nicht.

    Blut geleckt? ^^

    Shade Of Mine schrieb:

    Das Problem dass ich damit habe ist, dass jedes Implementationsdetail öffentlich ist. Es ist zB nicht möglich beim Ursprung des Kreises plötzlich auf double zu wechseln wenn ich vorher int verwendet habe. Oder zB auf __int64 statt __int32 oder sowas. Weil überall in meinem Clientcode eben direkt PunktXY verwendet wird. Und PunktXY kann ich ja nicht ändern, weil das von allen anderen Klassen auch verwendet wird.

    Hier kommen dann quasi Templates ins Spiel...

    Shade Of Mine schrieb:

    Was cool ist, ist printKoordinaten. Das ist richtig cool. Aber das ist jetzt die einzige Situation wo ich was cooles gefunden habe.

    Immerhin.

    Manchmal übersieht man aber Dinge, die nicht gefunden werden können, weil sie nicht da sind.
    Ist Dir mal aufgefallen, dass Du Dir keine Membernamen ausdenkst?

    Das finde ich richtig cool.

    Shade Of Mine schrieb:

    Gibt es eine Sprache in der dein Ansatz verfolgbar ist? Du musst ja schon Erfahrung damit haben: hast du vielleicht Code wo du das Umgesetzt hast? Wie gesagt - Sprache ist mir egal. Aber in C++ kommen wir hier auf keinen Grünen Zweig.

    Ich kann mich nur selbst zitieren:

    Xin schrieb:

    Ich kann Dir nicht sagen, ob es dafür eine Sprache gibt, mir ist keine bekannt, sonst würde ich die ja verwenden und nicht selbst eine schreiben. ^^

    Wobei... würde ich wohl doch... ich habe ja auch ein paar andere verrückte Ideen und Spaß macht's auch noch. ^^



  • @Xin: gibt es vielleicht irgendwo ein Dokument oder irgendwas, was deine Sprache mal skizziert? Deine Ideen diesbezüglich interessieren mich wirklich (du hast ja Verschiedenes auch schon in anderen Threads angedeutet). Oder könntest du mal ein paar Grundgedanken andeuten? Was sind noch grundlegende Neuerungen gegenüber existierenden Sprachen? Das muss ja nicht ausformuliert sein, nur damit man mal einen Eindruck bekommt.
    Und hast du dich mal mit funktionalen Sprachen beschäftigt? Offenbar betreibst du ja viele Gedanken in Richtung Sprachdesign und meines Erachtens haben funktionale Sprachen (z.B. Haskell) eine innewohnende Eleganz, die sowohl OOP als Konzept, als auch die mir bekannten imperativen und objektorientierten Sprachen nicht ansatzweise erreichen. Oder gibt es andere Gründe, warum deine Sprache objektorientiert/imperativ ist?



  • Ich habe jetzt probiert eine Klasse Auto zu schreiben und puh. Jetzt eck ich nur noch an.

    http://ideone.com/UDxDDd

    Ich bekomme die Klasse Tank nicht so hin, dass sie von Fuellstand und Maximalstand erbt, aber diese Beiden Werte zueinander in beziehung setzt. So will ich, dass der Fuellstand nie unter 0 aber auch nie üeber Maximalstand geht.

    Das ist aber alles doof, weil ja Auto ebenfalls zugriff auf Maximalstand und Fuellstand haben will. Auf herkömmliche Art ist das ganze ja trivial - aber hier artet das etwas aus.

    Ich habe irgendwie das Gefühl dass das Open Closed Principle hier nicht so ganz passen will. Wenn ich Komponenten schreibe, dann sind die immer ziemlich genau auf meinen Usecase zugeschnitten. Ich muss zB Fuellstand von meinem ChangeObserver erben lassen noch bevor ich eine Klasse Tank habe. Was wenn ich später drauf komme ich will zB Geschwindigkeit ebenfalls Observen? Dann muss ich ja Geschwindigkeit ändern... Irgendwie muss da noch was besseres her.

    Was auch total ärgerlich ist, ist dass die Werte die ich überall zuweise keinen Namen haben. Es ist immer Integer::value und nie PS oder kmh oder xCoord. Das macht den Code etwas doof zu warten.

    Sobald ich nicht public erbe, habe ich Probleme. Solange ich alle Klassen immer public erben lassen läuft das ganze eigentlich relativ flüssig. Aber wenn ich Beschränkungen machen will, dann wird es tricky. Und Implementierungsdetails verstecken kann ich auch nicht. Auch Templates helfen mir da kaum weiter - weil ich vorher genau wissen muss was ich verstecken will und was nicht (ich kann nur ja nur wenige Details verstecken - über eine höhere Abstraktionsebene). Dadurch verliere ich aber wieder die "composability" also die Möglichkeit nach belieben die Komponenten zusammenzustöpseln.

    Vielleicht habe ich heute Abend noch eine Erleuchtung aber bis jetzt sehe ich keinen Sinn.

    Die ganze Zeit wünsche ich mir einfach nur Mixins - die lösen diese Probleme. Hast du dir schonmal Rubys Mixins oder zB JavaScripts prototype angesehen?



  • ipsec schrieb:

    @Xin: gibt es vielleicht irgendwo ein Dokument oder irgendwas, was deine Sprache mal skizziert? Deine Ideen diesbezüglich interessieren mich wirklich (du hast ja Verschiedenes auch schon in anderen Threads angedeutet).

    Es gibt solche Dokumente teilweise und ich übertrage sie wenn ich Zeit habe in ein dafür vorgesehenes Wiki. Da ich alleine daran arbeite, auch regelmäßig auf neue Ideen stoße, ist das aber alles nicht wirklich auf aktuellem Stand.
    Für gute Doku fehlt mir leider die Zeit, was schade ist, denn falls ich mal unter die Bahn gerate, gibt's halt kein aktuelles Backup.

    ipsec schrieb:

    Oder könntest du mal ein paar Grundgedanken andeuten? Was sind noch grundlegende Neuerungen gegenüber existierenden Sprachen? Das muss ja nicht ausformuliert sein, nur damit man mal einen Eindruck bekommt.

    Zwei Dinge: Erstens will ich eigentlich noch gar keinen öffentlichen Eindruck vermitteln und zweitens würde ich mich nicht soweit rauslehnen um von "grundlegenden Neuerungen" zu sprechen. Ich will nicht das Programmieren neu erfinden. Ich bin faul, ich will wenig Arbeit haben (jaja, ich weiß, klever dann eine Programmiersprache zu schreiben, um weniger programmieren zu müssen...) und ich will dass die Sprache mich gut unterstützt.

    Zu Anfang habe ich Leute gesucht, die sich dafür begeistern konnten. Habe ich gefunden. Leider sind sie entweder nicht in der Lage zu helfen oder wissen, dass sie diese Zeit auch gegen Geld in andere Projekte stecken können. Dann habe ich vielversprechende und interessierte Leute in C++ unterrichtet. Die einen entwickelten andere Interessen, die anderen bemerkten, dass man Wissen auch teuer verkaufen kann und andere einfach besser zahlen können als ich. Kurz: der Aufwand andere zu involvieren kostet mich viel Zeit, ist aber nicht sonderlich produktiv bisher.

    Ich habe da inzwischen viel eigene Zeit reingesteckt - in mein Projekt und in andere Leute. Die anderen Leute habe ich inzwischen aufgegeben, mein Projekt steckt jetzt in den Kinderschuhen und lernt gerade laufen.

    ipsec schrieb:

    Und hast du dich mal mit funktionalen Sprachen beschäftigt? Offenbar betreibst du ja viele Gedanken in Richtung Sprachdesign und meines Erachtens haben funktionale Sprachen (z.B. Haskell) eine innewohnende Eleganz, die sowohl OOP als Konzept, als auch die mir bekannten imperativen und objektorientierten Sprachen nicht ansatzweise erreichen. Oder gibt es andere Gründe, warum deine Sprache objektorientiert/imperativ ist?

    Ich war mal eine Zeit recht fit in Prolog und vor laaaanger, laaaaaaaanger Zeit in einer LISP ähnlichen Sprache.

    Ich denke, die Idee funktionaler Sprachen zu verstehen. Eine Garantie gebe ich darauf nicht. 😉
    Ich wollte mir letztes Jahr Scala aneignen, was leider an Zeitmangel auf dieses Jahr verschoben wurde. Haskell habe ich mir nur kurz angesehen, steht auch auf meiner Todo-Liste, aber deutlich weiter hinten. Mir sind die Vorteile von unveränderlichen Werten bei funktionalen Programmiersprachen bewusst.

    Warum imperativ? Ich glaube nicht an die Einordnung in Paradigmen. Das Paradigma "OOP" ist nur ein Design-Pattern und funktionale Programmierung ist eigentlich nichts weiter als eine Sonderform von imperativer Programmierung, aufgrund der gewisse Grundannahmen getroffen werden können, die sich zur Optimierung eignen. Im Prinzip lässt sich das ganze auf imperative Programmierung zusammenschieben und die Programmierung, wie sie in C++/Java/C# getätigt wird, lässt sich ebenfalls deutlich reduzieren.

    Immer wieder gerne gesehen sind Ausdrücke. Der Wunsch eines jeden (mir bekannten) Programmiersprachendesigners ist alles auf den Ausdruck zu reduzieren (gut, das sind nicht viele...). Mir sagt das auch zu, denn wenn alles ein Ausdruck ist, muss die Syntax einerseits sehr flexibel sein, andererseits erlaubt es kaum Ausnahmen.

    Imperativ ist Einsteigern leicht zu vermitteln und praktisch für viele einfache Fälle. Funktional als Sonderform ist eher für Fortgeschrittene. Ich möchte nicht auf 'leicht vermittelbar' und 'praktisch für einfache Fälle' verzichten.

    Shade Of Mine schrieb:

    Ich habe jetzt probiert eine Klasse Auto zu schreiben und puh. Jetzt eck ich nur noch an.

    Das wird ein langer Tag, ich komme hier heute zu nix... :->
    Musst Du nicht auch zwischenzeitlich irgendwas für Geld tun? 😃

    Vielleicht wirfst Du die Kreise etc. auch mal weg oder lagerst sie zumindest aus.
    Die Fehlermeldungen für operator+ sehen... merkwürdig aus. Sicher dass das Macro sauber ist?

    Keine Ahnung, ob ich heute Abend noch in der Lage bin, den Quellcode auszuprobieren. Am liebsten würde ich da direkt mitspielen, aber diese Woche ist noch High-Life in Tüten. Keine Ahnung, wann ich heute überhaupt noch zu Hause eintreffe. Mann, Mann, Mann... 😉

    Shade Of Mine schrieb:

    Ich bekomme die Klasse Tank nicht so hin, dass sie von Fuellstand und Maximalstand erbt, aber diese Beiden Werte zueinander in beziehung setzt. So will ich, dass der Fuellstand nie unter 0 aber auch nie üeber Maximalstand geht.

    Das ist aber alles doof, weil ja Auto ebenfalls zugriff auf Maximalstand und Fuellstand haben will. Auf herkömmliche Art ist das ganze ja trivial - aber hier artet das etwas aus.

    Das Auto hat doch Zugriff auf den Tank und der Tank hat einen Füllstand und einen Maximalstand!?

    Shade Of Mine schrieb:

    Ich habe irgendwie das Gefühl dass das Open Closed Principle hier nicht so ganz passen will. Wenn ich Komponenten schreibe, dann sind die immer ziemlich genau auf meinen Usecase zugeschnitten. Ich muss zB Fuellstand von meinem ChangeObserver erben lassen noch bevor ich eine Klasse Tank habe. Was wenn ich später drauf komme ich will zB Geschwindigkeit ebenfalls Observen? Dann muss ich ja Geschwindigkeit ändern... Irgendwie muss da noch was besseres her.

    Ich kann jetzt nicht viel dazu sagen und für mal kurz ohne Compiler drüber zu gucken ist der Code etwas lang viel für meinen Bio-Stack.

    Shade Of Mine schrieb:

    Was auch total ärgerlich ist, ist dass die Werte die ich überall zuweise keinen Namen haben. Es ist immer Integer::value und nie PS oder kmh oder xCoord. Das macht den Code etwas doof zu warten.

    Du hast Motor als PS definiert. Eigentlich hättest Du wie bei der Geschwindigkeit eine Klasse PS haben müssen, die dann in Motor übergeht, so dass Du zu

    auto = PS(123);
    

    kommst.

    Shade Of Mine schrieb:

    Sobald ich nicht public erbe, habe ich Probleme. Solange ich alle Klassen immer public erben lassen läuft das ganze eigentlich relativ flüssig. Aber wenn ich Beschränkungen machen will, dann wird es tricky.

    Ich bin mir noch nicht ganz sicher, ob ich public const protected mutable sauber ausdrücken kann. Ich kann es mir im Kopf vorstellen, auf die Idee das in C++ zu formulieren, bin ich aber bisher nicht gekommen. Vielleicht aus Angst das gut zu finden und dann in die produktive Programmierung zu übernehmen, um Realworld-Erfahrungen damit zu sammeln. ;-D

    Shade Of Mine schrieb:

    Und Implementierungsdetails verstecken kann ich auch nicht. Auch Templates helfen mir da kaum weiter - weil ich vorher genau wissen muss was ich verstecken will und was nicht (ich kann nur ja nur wenige Details verstecken - über eine höhere Abstraktionsebene). Dadurch verliere ich aber wieder die "composability" also die Möglichkeit nach belieben die Komponenten zusammenzustöpseln.

    Vielleicht habe ich heute Abend noch eine Erleuchtung aber bis jetzt sehe ich keinen Sinn.

    Die ganze Zeit wünsche ich mir einfach nur Mixins - die lösen diese Probleme. Hast du dir schonmal Rubys Mixins oder zB JavaScripts prototype angesehen?

    Ruby Mixins habe ich noch nicht angesehen. Ruby streitet sich mit ObjC um Platz 2 auf meiner Sprachen-Lern-Liste. Das Problem ist dann noch, wirklich Erfahrungen mit den Konstrukten zu sammeln, weil meine Projekte nunmal fast alle in C++ ablaufen und ich keine Zeit für zusätzliche erwähnenswerten Projekte hätte, wo man die große Erfahrung sammeln könnte.

    JavaScript Prototypes habe ich mal vor längerer Zeit programmiert, da müsste ich aber nochmal nachgucken, da ich von JavaScript eher panisch abgeschreckt als inspiriert war. (Passend dazu)



  • Ich habe den Ansatz komplett verworfen. Ich hab ihm heute jede freie Minute waehrend der Arbeit gegeben und muss leider sagen: ich komme nichtmal soweit, dass ich volkards Kritik an dem Ansatz bearbeiten kann. Ich scheitere vorher.

    Der Code ist sicher schlecht und koennte 100mal besser gemacht werden. Aber das einzige was sich gut angefuehlt hat an dem Ansatz war, sagen zu koennen: Das Ding hat jetzt einen Namen und somit ein getName/setName.

    Aber das ist mit Vererbung nicht gut ausgedrueckt. Mir ist alter Code eingefallen den ich vor langer langer Zeit mal geschrieben habe: Container Adapter.

    Wenn ich einen vector habe und ich will den jetzt aber nur noch sortiert haben, auch wenn ich spaeter nochwas hinzufuege - wie tue ich das? Meine Loesung waren damals Adapter:

    template<typename ContainerT>
    class sorted_container_mutable :
    	public interface_add_sorted_insert<
    		interface_add_sequence_access<
    			interface_base<
    				ContainerT
    			>
    		>
    	> {
    };
    
    template<typename ContainerT>
    class sorted_container :
    	public interface_add_sorted_insert<
    		interface_add_const_sequence_access<
    			interface_base<
    				ContainerT
    			>
    		>
    	> {
    };
    

    einmal sorted und einmal mutable_sorted.
    Dazu natuerlich noch die ganzen Interface Operationen:
    http://ideone.com/IvaTTL

    Sowas fuehlt sich irgendwie besser an.
    Genauso haette ich auch gerne Kreis nicht von PunktXY abgeleitet sondern nur einen Accessor zu PunktXY ueber Kreis drueber gelegt. Nur ist Vererbung hier nicht so toll. IMHO schreit der Ansatz nach Mixins.

    Sowas sieht da etwas besser aus IMHO: http://ideone.com/sPY3Xs
    Sofern man rausfindet wie man ctors sinnvoll vererben kann.
    Sowas ist aber nur billiger Mixin Ersatz

    Es gibt viele Sprachen die Mixins bieten. Ich persoenlich mag den JavaScript Ansatz - JavaScript hat natuerlich viele WTFs - keine Frage. Aber alles in allem ist es ne ordentliche Sprache. Aber du solltest dir das Konzept Mixin wirklich naeher ansehen, denn nachdem ich jetzt einige Stunden mit dem vererbungskonzept rumprobiert habe - und nichtmal weit genug gekommen bin um das Konzept als ganzes zu sehen, weil es schon im Ansatz voller Probleme ist - muss ich sagen: Mixins wuerden die meisten der Probleme sofort loesen.


Anmelden zum Antworten