@kingruedi



  • DrGreenthumb schrieb:

    also eigentlich doch genau wie der c++ destruktor eines objekts auf dem stack?

    das ist dann doch wunderbar. Sogar besser als in C++ wo man für sowas erst eine Klasse erstellen muss.

    nein, das nehme ich zurück. Schließe mich dem an, dass das Objekt für seine Löschung verantworlich sein sollte, ohne das der Aufrufer extra cleanup() aufrufen muss.
    Das unwind-protect ist eigentlich auch nur so praktisch, weil man's zB. in ein with-macro wrappen kann.



  • Aber irgendwie geht das halt nicht immer. Wenn man ein Strategiespiel hat, dann weiß ich nicht, wann ich ne Einheit aufräumen kann. Wenn sie gestorben ist? Aber vielleicht wird sie noch von einer anderen Einheit anvisiert (oder einem Geschoss). Wenn sie verfault ist? Wer garantiert, dass sie nirgendwo mehr referenziert ist? Sehr gefährlich, sie aufzuräumen.

    Das Problem versteh ich nicht. Wenn die Einheit ein RefCounted Objekt ist, kann sie jeder wegschmeißen, wenn er sie nicht mehr braucht. Wenn sie der letzte wegschmeißt, wird sie freigegeben. Ist doch ok? Wo ist das RefCounting unzuverlässig?

    Das Problem das ich hier sehe ist eher ein semantisches. Ist eine zerstörte verfaulende Einheit noch eine Einheit mit einem bestimmten Attribut oder sind das andere Objekte? Das kann man aber doch über Vererbung und another level of indirection lösen und hat nix mit aufräumen zu tun.

    Manchmal fänd ich ein finally schon auch in c++ ganz cool. Hin und wieder kommts vor, dass ich alleine zum Zweck des exception-sicheren aufräumens eine in die Funktion eingebettete Klasse schreiben muss. Im Dtor steht dann das, was im finally-Block stehen würde. Nur muss ich viel mehr tippen: Klassendeklaration, Ctor, dem referenzen auf die zusetzenden Objekte gegeben werden etc.



  • kartoffelsack schrieb:

    Manchmal fänd ich ein finally schon auch in c++ ganz cool. Hin und wieder kommts vor, dass ich alleine zum Zweck des exception-sicheren aufräumens eine in die Funktion eingebettete Klasse schreiben muss. Im Dtor steht dann das, was im finally-Block stehen würde. Nur muss ich viel mehr tippen: Klassendeklaration, Ctor, dem referenzen auf die zusetzenden Objekte gegeben werden etc.

    Tust du dir einen ScopeGuard schreiben oder den von Alexandrescu nehmen.



  • ah ja: http://www.cuj.com/documents/s=8000/cujcexp1812alexandr/

    sowas hab ich mir auch schon selber gebaut. Nur hatte ich da immer Probleme mit konstanten Objekten, Referenzen ... und war weit hausbackener natürlich.
    Sieht gut aus. Vielen Dank!

    Nur, hm, wo kann ich das ganze denn fertig downloaden? Der Link, der hier angegeben ist http://zfx.p15139172.pureserver.info/DisplayThread.php?TID=15376 funzt nicht. Und in welcher Boost-Bibliothek kann ich das ganze finden?



  • kartoffelsack schrieb:

    Das Problem versteh ich nicht. Wenn die Einheit ein RefCounted Objekt ist, kann sie jeder wegschmeißen, wenn er sie nicht mehr braucht. Wenn sie der letzte wegschmeißt, wird sie freigegeben. Ist doch ok? Wo ist das RefCounting unzuverlässig?

    Bei zirkulären Abhängigkeiten, die keineswegs eine Seltenheit sind.

    Das Problem das ich hier sehe ist eher ein semantisches. Ist eine zerstörte verfaulende Einheit noch eine Einheit mit einem bestimmten Attribut oder sind das andere Objekte? Das kann man aber doch über Vererbung und another level of indirection lösen und hat nix mit aufräumen zu tun.

    Ist doch egal was es ist. Wenn sie irgendwo referenziert wird, kann ich sie nicht gefahrlos deleten, und zwar so lange nicht, bis auch der letzte ->isDead() gemacht hat und die Referenz aufgehoben hat. Wenn das Teil bereits deleted ist, kann ich isDead() jedenfalls nicht mehr fragen. Indirektionen sind lame und was hat Vererbung damit zu tun? Ne, lieber nichts deleten und beim Spielende alles kicken. Dann kann nichts passieren. Wenigstens ein Herzinfarkt-Risiko weniger.

    Manchmal fänd ich ein finally schon auch in c++ ganz cool.

    Naja, ich nicht. aber zumindest ein paar namhafte Sprachdesigner anscheinend schon, also Geschmackssache vielleicht.



  • Bashar schrieb:

    kingruedi: Dann hältst du nicht mehr viel von Lisp? Da gibts ja auch nur unwind-protect als Gegenstück zu try..finally.

    In Lisp bin ich bisher nicht auf das Problem gestoßen. Aber es ist ja nicht unbedingt ein K.O.-Kriterium für eine Programmiersprache. Es lässt sich bei einem Garbage Collector wohl nicht anders lösen. Mich stört das in Java wahrscheinlich einfach, weil ich Java zu sehr aus meiner C++-Sicht sehe. Bei Lisp komm ich mit der C++-Sicht eh nicht weiter 😉 (das ist ja schon bei Java kaum möglich ;))

    Optimizer schrieb:

    Bei zirkulären Abhängigkeiten, die keineswegs eine Seltenheit sind.

    Öhm, was genau sind zirkuläre Abhängigkeiten?

    Ist doch egal was es ist. Wenn sie irgendwo referenziert wird, kann ich sie nicht gefahrlos deleten, und zwar so lange nicht, bis auch der letzte ->isDead() gemacht hat und die Referenz aufgehoben hat.

    Bei deinem Beispiel kann man das Problem doch sehr gut mit Referenzcounting lösen.



  • kingruedi schrieb:

    Optimizer schrieb:

    Bei zirkulären Abhängigkeiten, die keineswegs eine Seltenheit sind.

    Öhm, was genau sind zirkuläre Abhängigkeiten?

    A hält eine Referenz auf B und B eine auf A; damit verhindern sie, daß sie jemals freigegeben werden können, wenn der RC kein "cyclic"-flag hat, o.ä.. Dann kann man aber auch gleich Mark&Sweep nehmen. RC bringt es eigentlich nur, wenn man richtig viele (oder große) Daten hat, sonst ist jeder halbwegs moderne GC sogar schneller(!).



  • kingruedi schrieb:

    Bei deinem Beispiel kann man das Problem doch sehr gut mit Referenzcounting lösen.

    Nein, wegen den zirkulären Abhängigkeiten leider nicht. Daniel E. hat die gegenseitige Abhängigkeit, ein Spezialfall, schon genannt. Gegen andere Fälle hilft AFAIK auch kein flag im Smartpointer.



  • Naja, kommt drauf an wie man modelliert.

    Ein Objekt, das einen Smart-Pointer auf einen anderen hat, ist ein Besitzer dieses Objekts.
    In Deinem Strategiespiel ist aber die Einheit A, die die Einheit B im Visier hat aber nicht der Besitzer von B. Sie kennt B. Deswegen hätte sie auch keinen Ref-Counted Pointer auf B sondern nen normalen (oder nen weak_ptr).
    B (und jedes andere Objekt) wäre eher an einer zentralen Stelle registriert. Diese würde über ein Observer-Pattern von B darüber informiert werden, wenn B zerstört wird. Analog könnte A direkt von B oder von der zentralen Instanz informiert werden (das ist die zusätzliche Indirektionsebene, die ich meinte).
    Hört sich für das banale Beispiel jetzt vielleicht kompliziert an, aber ich denke in einer realen Anwendung müsste man sowieso mit einer zentralen Registratur und div. Obervern arbeiten.
    D.h. ich bin mir nicht sicher, ob das Problem mit den zirkulären Abhängigkeiten nicht theoretisch oder ein Hinweis auf unsauberes Design ist. Mir persönlich ist es auch erst einmal in der Bib von nem Kollegen begegnet. Und da war es mE falsch eingesetzt.
    Aber ok, mit ner GB braucht man sich von der Seite erstmal keine Sorgen zu machen.

    PS.: Wie merkt eigentlich eine GC, dass es sich um zirkuläre Abhängigkeiten handelt.



  • kartoffelsack schrieb:

    ... (und jedes andere Objekt) wäre eher an einer zentralen Stelle registriert. Diese würde über ein Observer-Pattern von B darüber informiert werden, wenn B zerstört wird. Analog könnte A direkt von B oder von der zentralen Instanz informiert werden (das ist die zusätzliche Indirektionsebene, die ich meinte).

    Ja, könnte ne Lösung sein. Ist aber echt lame, weil ne Einheit während ihrer Lebenszeit ungefähr 200mal ein anderes Objekt anvisiert und sich jedesmal beim Observer an- und abmelden muss. Wenn es nicht um Gigabyteweise RAM geht, bevorzuge ich für so einen Fall die Lösung mit am Ende freigeben (Spielende != Programmende).
    Nicht dass wir uns misverstehen: Ich komm schon irgendwie mit der Speicherverwaltung klar. 😉 Es gibt halt nur Fälle wo ein GC einfach mehr rockt und das ist so einer. Effizienter als der Observer mit Millionen von Zustandsänderungen während eines Spiels wäre er mit hoher Wahrscheinlichkeit.

    PS.: Wie merkt eigentlich eine GC, dass es sich um zirkuläre Abhängigkeiten handelt.

    Der GC sucht erreichbare Objekte. Es kümmert ihn nicht, wenn zwei unerreichbare sich gegenseitig referenzieren, weil er zu beiden nie hin kommt. Er geht von ein paar roots aus und verfolgt alle Pointer um erreichbare Objekte zu bestimmen. Wurzeln sind u.a.:
    - Prozessorregister
    - Alle Stackframes aller Threads

    Er hat von allen Dingen (wie Stackframes) statische Metadaten, die sagen, welche Bytes Pointer sind und welche nicht. Es gibt auch GCs (welche für Standard C++), die das nicht haben. Die sucken aber eh, weil sie Objekte nicht verschieben können, die lass ich mal außen vor. Wenn ich GC sage, meine ich immer einen männermäßigen GC. 😉 🤡



  • Optimizer schrieb:

    Wenn ich GC sage, meine ich immer einen männermäßigen GC. 😉 🤡

    Pah, ECHTE Männer brauchen keinen GC 🤡 1.

    1~Der Autor behält sich vor trotzdem Sprachen zu verwenden die einen garbage-collector besitzen, obwohl es nicht männlich ist.~



  • Automatismus ist recht praktisch



  • Nur der Vollständigkeit halber

    Und in welcher Boost-Bibliothek kann ich das ganze finden?

    z.Z. in boost/multi_index/detail/scope_guard.hpp

    Kann aber gut sein, dass die da ne eigene Bib für machen oder es 'offiziell' woanders hintun. Weil die momentane Implementierung ist speziell für boost::multi_index.



  • kartoffelsack schrieb:

    Nur der Vollständigkeit halber

    Und in welcher Boost-Bibliothek kann ich das ganze finden?

    z.Z. in boost/multi_index/detail/scope_guard.hpp

    Kann aber gut sein, dass die da ne eigene Bib für machen oder es 'offiziell' woanders hintun. Weil die momentane Implementierung ist speziell für boost::multi_index.

    das kann aber dauern. die erste diskussion auf comp.lib.boost.devel über Andrei's ScopeGuard ist von 2002 und irgendwie hat sich bisher trotzdem noch niemand die arbeit gemacht um ScopeGuard offiziell zu machen.


Anmelden zum Antworten