Ressourcen-Verwaltungsklasse
-
Nexus schrieb:
Wenn ich das richtig sehe, verhält sich
resource_slotähnlich wie meinshared_ptr, nur dass zusätzlich die Logik fürs Laden implementiert ist.Nein, kann man grundsätzlich so nicht stehen lassen. Grundsätzlich haben sie nur den Counter gemeinsam, aber nicht mal der ist gleich implementiert und bedeutet auch nicht unbedingt das Gleiche. Ich habe hier ganz bewusst auf
shared_ptrverzichtet, um einige Nachteile loszuwerden. Mit einemshared_ptrkann man ganz einfach Memory-Leaks erzeugen. Man muss ihn einfach nur irgendwo abspeichern. Bei meinem Code verwendet man normalerweise den Guard, welcher garantiert, dass der Lock immer wieder aufgehoben wird. Theoretisch könnte man direkt die Funktionlockaufrufen, nur sollte man dies nicht tun, da man dann gegen RAII verstösst. Ich habe mich da ein wenig an Boost.Thread mit denmutex-Klassen undlock_guardorientiert. Auch dort könnte man direktlockaufrufen, macht aber niemand. Man hat aber die Freiheit eigene Klassen zu erstellen, welche ein anderes Guard-Verhalten haben.Nexus schrieb:
lock()undunlock()wären wohl die Äquivalente zu meinenAcquire()undRelease()-Funktionen.Ja, mit dem Unterschied, dass ich das Bekanntmachen der Ressource von Acquire trenne. Die Gründe dafür stehen weitern unten.
Nexus schrieb:
Allerdings weiss ich nicht, ob sich die Indirektion über
resource_slotals eigene Klasse bei mir lohnt.Wieso nicht? Wo würdest du den Code für das Laden und Entladen dann hinschreiben? In den Manager? Das ist aber keine saubere Aufgabentrennung

Nexus schrieb:
Bei deinem Vorschlag müssen alle Benutzer einer Ressource ihr Handle haben, oder? Ich versuche, dass auch gleiche Anfragen von unterschiedlichen Orten zum gleichen Handle (wobei das bei mir eher eine ID ist) führen. Also dass die Anforderungsreihenfolge keine Rolle spielt und kein bestimmter Benutzer zuerst das Handle erzeugen muss.
Verstehe ich dich richtig, dass du String-Literale dazu verwenden willst, als ID herzuhalten? Davon würde ich dringend abraten! String-Literale als IDs haben in etwa die gleichen Nachteile wie Magic Numbers. Wenn du zum Beispiel die ID verändern willst, dann musst du überall die String-Literale ändern gehen. Das Gleiche gilt auch für den Pfad in deiner Funktion Acquire. Viel Spass, wenn sich da was ändert :p
Die nächste Überlegung wäre dann, dass du die String-Literale irgendwo global bekannt machst, so dass du nur noch Variablen der Acquire Funktion übergeben musst. Nur kannst du das dann auch direkt mit den Handles machen, welche allerdings einen deutlich schnelleren Zugriff erlauben.
Und falls du dir überlegt hast, als ID den Pfad zum File zu nehmen, dann rate ich auch davon dringend ab. Nicht nur gilt das gleiche wie oben, wenn du den Pfad veränderst, sondern es gibt noch weitere Probleme bei der Eindeutigkeit von Pfaden:C:/hallo/test.jpg C:\hallo\test.jpg C:/hallo/Test.jpgSind unter Windows alles gleiche Pfade, unter C++ dann aber äusserst schwer zu vergleichen, bzw. derren Gleichheit festzustellen. Fehler sind da vorprogrammiert.
Grüssli
-
Dravere schrieb:
Ich habe hier ganz bewusst auf
shared_ptrverzichtet, um einige Nachteile loszuwerden. Mit einemshared_ptrkann man ganz einfach Memory-Leaks erzeugen. Man muss ihn einfach nur irgendwo abspeichern. Bei meinem Code verwendet man normalerweise den Guard, welcher garantiert, dass der Lock immer wieder aufgehoben wird.Der Vorteil existiert beim Guard aber auch nur solange, wie du ihn als lokale Variable in Funktionen benutzt. Sobald man etwas flexibler sein will – und das will ich, z.B. habe ich Klassen, die durch ihre ganze Lebensdauer einen rohen, passiven Zeiger auf ein Bild beinhalten – muss man den Guard auch als Klassenmember verwenden und kann ebenso "Leaks" erhalten, wenn das Objekt nicht oder erst spät zerstört wird. Also eigentlich gleich wie beim
shared_ptr. Solange man diesen nur wiescoped_ptrbenutzt, verschwindet die Problematik. Mit ihr aber auch die Flexibilität.Dravere schrieb:
Nexus schrieb:
Allerdings weiss ich nicht, ob sich die Indirektion über resource_slot als eigene Klasse bei mir lohnt.
Wieso nicht?
Weil bei mir jede neue Klasse zuerst einmal eine Bewährungsfrist hat.

Nein, eigentlich war das ja mehr eine Folge aus meiner Annahme, dass sich
resource_slotähnlich wie einshared_ptrverhalte, und man eventuell was Fertiges nehmen könne. Aber genau durchdacht habe ich das wie angetönt noch nicht.Dravere schrieb:
Verstehe ich dich richtig, dass du String-Literale dazu verwenden willst, als ID herzuhalten? Davon würde ich dringend abraten! String-Literale als IDs haben in etwa die gleichen Nachteile wie Magic Numbers.
Das hatte ich am Anfang (siehe 1. Post), aber es gibt eben aus verschiedenen Gründen Probleme. Danke auch für den Hinweis mit der Pfad-Mehrdeutigkeit. Ich versuche daher etwas von diesem Ansatz wegzukommen, ohne gleich die Vorteile zu verlieren.

Ich sehe halt nach wie vor keine saubere Lösung, wie ein Ressourcenbenutzer bei der Anforderung auf eine bestehende Ressource zugreift, wenn diese bereits existiert, und ansonsten automatisch eine neue kriegt. Um es nochmals an C++-Code zu verdeutlichen:
// 1. wichtige Eigenschaft void Function(unsigned int width, unsigned int height) { // Sei ImagePtr irgendeine Brücke zum Zugriff auf die eigentliche Ressource ImagePtr p = resMgr.AcquireImage(width, height); // Wenn nun das Bild mit width und height bereits angefordert wurde, dieses // erhalten. Ansonsten soll resMgr implizit ein neues anfordern. }// 2. wichtige Eigenschaft void Function(unsigned int width, unsigned int height) { // Nun will ich zwar ein Bild mit einer Grösse anfordern, die // bereits existiert. Da ich es aber für einen anderen Zweck benötige // (und es dabei verändere), darf es nicht das gleiche wie p sein. // Deshalb habe ich mir überlegt, durch einen zusätzlichen String // Eindeutigkeit zu schaffen. ImagePtr p = resMgr.AcquireImage(width, height, "use case 1"); ImagePtr q = resMgr.AcquireImage(width, height, "use case 2"); // q != p, die beiden referenzieren unterschiedliche Bilder }Ich habe bisher keine schöne Lösung gefunden, welche diese Semantik erfüllen kann, ohne die von dir und mir erwähnten Nachteile zu besitzen.
-
Ich glaube, es ist das Beste, wenn ich zuerst nochmals ein paar Fragen stelle

Nexus schrieb:
Ich sehe halt nach wie vor keine saubere Lösung, wie ein Ressourcenbenutzer bei der Anforderung auf eine bestehende Ressource zugreift, wenn diese bereits existiert, und ansonsten automatisch eine neue kriegt. Um es nochmals an C++-Code zu verdeutlichen:
...Ich habe bisher keine schöne Lösung gefunden, welche diese Semantik erfüllen kann, ohne die von dir und mir erwähnten Nachteile zu besitzen.
Also moment einmal, nur zur Klarstellung. Du brauchst 4 Eigenschaften:
1. Ein Bild soll in einer bestimmten Grösse geladen werden. Dabei soll ein Cache angewandt werden.
2. Falls das Bild nicht mehr benötigt wird, soll es automatisch aus dem Cache verschwinden.
3. Es soll eine Möglichkeit geben, dasselbe Bild mit gleicher Grösse erneut zu laden, es aber auch in den Cache einzufügen.
4. Ein Bild soll nicht nur in einer Funktion verwendet werden, sondern soll auch in einer Klasse gespeichert werden können.Kommt mir langsam so vor, als ob du ein wenig viele Fliegen mit einer Klatsche treffen möchtest

Wie ist das bei der dritten Eigenschaft. Muss dieses Bild wirklich auch in den Cache abgelegt werden? Sonst könnte man da ein "Force New" implementieren, wodurch der Cache immer umgangen wird. Und was machst du mit diesen Bildern? Veränderst du diese auch oder liest du nur aus ihnen, bzw. stellst sie dar? Wieso brauchst du eine weitere Kopie eines Bildes gleicher Grösse? Wann und wieso sollen aktuell nicht mehr benötigte Bilder entladen werden? Soll dies überhaupt automatisch passieren? Zudem hast du erst später von der Bildgrösse angefangen zu reden, am Anfang hiess es noch, dass da auch Sounds, Schriftarten, usw. drüber verwaltet werden sollen. Wie soll also in diesen Fällen vorgegangen werden? Probierst du hier alles mögliche mit unterschiedlichen Eigenschaften über einen Kamm zu bekommen? Kannst du vielleicht ein wenig mehr Kontextinformationen liefern?Ich bin inzwischen etwas verwirrt über deine Ziele

Grüssli
-
Dravere schrieb:
Probierst du hier alles mögliche mit unterschiedlichen Eigenschaften über einen Kamm zu bekommen?
Schon ein wenig.

Ich versuche halt, eine möglichst generische und flexible Ressourcenverwaltung ohne viel Boilerplate-Code für den Benutzer zu implementieren. Vorläufig sind vor allem Bilder wichtig, aber später möchte ich die Verwaltung auf weitere Ressourcentypen ausweiten. Wobei das momentan keine Priorität hat, eventuell würde ich dann auch separate Klassen verwenden. Zuerst einmal Bilder sauber hinbekommen.

Um einen konkreten Anwendungsfall zu beschreiben: Ich habe GUI-Buttons, deren View intern leere Bilder anfordert, um darauf den Button zu zeichnen. Text und solche Sachen sind davon unabhängig, es geht wirklich nur ums Aussehen der Schaltfläche selbst. Nun müssen Buttons mit unterschiedlichen Dimensionen separat gezeichnet werden (ich will Verzerrungen durch Skalierung vermeiden). Da es wahrscheinlich oft vorkommt, dass mehrere Buttons die gleiche Grösse haben (und demnach gleich aussehen), sollen ihre Ressourcen geteilt werden. Das soeben Beschriebene bezieht sich auf
// 1. wichtige EigenschaftIm Weiteren gibt es z.B. Textfelder, die ein anderes Aussehen haben. Deshalb darf natürlich nicht das gecachte Bild vom Button genommen werden, selbst wenn es die gleiche Grösse hat. Darum auch der Versuch mit den String-IDs und
// 2. wichtige Eigenschaft
-
Das sind dann aber eher zwei getrennte Problemstellungen. Zum einen willst du Ressourcen eindeutig identifizieren und zum anderen einen Cache über die Grösse der Ressource implementieren. Daher habe ich auch das Gefühl, dass du die Kriterien in der falschen Reihenfolge betrachtest. Das erste Kriterium sollte sein, auf dein Beispiel bezogen, ob es eine Schaltfläche oder Textfeld ist. Das zweite Kriterium sollte dann die Grösse sein.
Bezogen auf meinen Beispielcode, könnte man sagen, dass zusätzlich zum lock/load/Acquire neben dem Handle noch die Grösse übergeben wird. Der Manager findet über das Handle den richtigen Slot und sagt diesem dann, dass er ein Bild mit der übergebenen Grösse zurückgeben soll. Der Slot verwendet dann intern einen Cache von seinem Bild mit unterschiedlichen Grössen.
Wobei für mich da immer noch "zwei" Fragen offen sein:
- Wann genau soll nun eine Ressource, bzw. eine im Cache gespeicherte Grösse, freigegeben werden? Nur weil eine Grösse aktuell nicht mehr benötigt wird, soll sie gleich entladen werden? Oder sollte man da vielleicht unterschiedliche Policies anwenden können? Welche wären das?
- Gibt es wirklich so viele Schaltflächen mit gleicher Grösse? Also gerade im GUI Bereich, habe ich das Gefühl, dass dies eher selten ist. Die Grösse wird ja auch meistens durch LayoutManager bestimmt. Und wenn du ein GUI machst, dann soll es wohl kein Standard GUI werden. Wenn ich mal raten darf: Ein Spiel-GUI? Ist man nicht gerade in einem Spiel oft sehr kreativ, was die GUIs betrifft, und verwendet somit unterschiedliche Grössen?
Grüssli
-
Dravere schrieb:
Das sind dann aber eher zwei getrennte Problemstellungen. Zum einen willst du Ressourcen eindeutig identifizieren und zum anderen einen Cache über die Grösse der Ressource implementieren. Daher habe ich auch das Gefühl, dass du die Kriterien in der falschen Reihenfolge betrachtest. Das erste Kriterium sollte sein, auf dein Beispiel bezogen, ob es eine Schaltfläche oder Textfeld ist. Das zweite Kriterium sollte dann die Grösse sein.
Bisher war es quasi ein einzelnes Kriterium. Der Schlüssel der Map/HashMap oder was auch immer bestand aus der Grösse und dem ID-String.
Dravere schrieb:
Wann genau soll nun eine Ressource, bzw. eine im Cache gespeicherte Grösse, freigegeben werden? Nur weil eine Grösse aktuell nicht mehr benötigt wird, soll sie gleich entladen werden? Oder sollte man da vielleicht unterschiedliche Policies anwenden können? Welche wären das?
Ich dachte, es könnte sinnvoll sein, Bilder im Cache zu erhalten, auch wenn sie nicht gleich benötigt werden. Wann genau die Freigabe erfolgt, steht noch nicht fest. Unterschiedliche Policies wären vielleicht eine gute Idee (z.B. sofort freigeben, gar nicht automatisch freigeben, Least Recently Used).
Dravere schrieb:
Gibt es wirklich so viele Schaltflächen mit gleicher Grösse? Also gerade im GUI Bereich, habe ich das Gefühl, dass dies eher selten ist. Die Grösse wird ja auch meistens durch LayoutManager bestimmt. Und wenn du ein GUI machst, dann soll es wohl kein Standard GUI werden. Wenn ich mal raten darf: Ein Spiel-GUI? Ist man nicht gerade in einem Spiel oft sehr kreativ, was die GUIs betrifft, und verwendet somit unterschiedliche Grössen?
Ja, ein Spiel-GUI. Naja, wenn ich z.B. in einem Menü mehrere Auswahlpunkte übereinander habe ("Spiel starten", "Optionen", "Credits", "Beenden", etc.), sieht es meiner Ansicht nach besser aus, wenn deren Schaltflächen gleich gross sind. Und auch bei einem allfälligen Ingame-GUI haben wahrscheinlich recht viele Schaltflächen die gleiche Grösse.
-
Nexus schrieb:
Bisher war es quasi ein einzelnes Kriterium. Der Schlüssel der Map/HashMap oder was auch immer bestand aus der Grösse und dem ID-String.
Was aber, wie du inzwischen wohl bemerkt hast, eher von Nachteil ist, weil das eine nichts mit dem anderen zu tun hat. Ich würde dir dringend zur Trennung von den Kriterien raten. So kannst du auch auf die Ressourcen unterschiedlich reagieren. Kannst gewissen Ressourcen einen Grössencache geben und anderen nicht. Du bist viel flexibler, wenn du die Kriterien voneinander trennst.
Nexus schrieb:
Wann genau die Freigabe erfolgt, steht noch nicht fest.
Wenn du dies nicht definierst, kann man auch nicht darüber diskutieren, was zurückgegeben werden soll. Schliesslich hängt dies stark davon ab, wie und wann die Freigabe erfolgen soll. Wenn die Freigabe zum Beispiel erst am Ende des Programmes erfolgt, bzw. wenn der Manager zerstört wird, kann man gleich rohe Zeiger zurückgeben.
Nexus schrieb:
Ja, ein Spiel-GUI. Naja, wenn ich z.B. in einem Menü mehrere Auswahlpunkte übereinander habe ("Spiel starten", "Optionen", "Credits", "Beenden", etc.), sieht es meiner Ansicht nach besser aus, wenn deren Schaltflächen gleich gross sind. Und auch bei einem allfälligen Ingame-GUI haben wahrscheinlich recht viele Schaltflächen die gleiche Grösse.
Ach, das sind doch altertümliche/klassische Vorstellungen

Ich finde es immer wieder gut, wenn in einem Spiel das GUI mit dem Hintergrund spielt. Zum Beispiel das Hauptmenü von CoH: http://i56.tinypic.com/r77h3a.pngSicher mag es ein paar GUI-Objekte geben, welche gleich gross sind und den gleichen Hintergrund haben, aber wieviele sind das und wie gross sind die? Schlussendlich, nehme ich an, geht es dir darum, dass du die Objekte nicht immer neu zeichnen musst. Da frage ich mich, ob es nicht klüger wäre, diesen Cache pro Objekt einzuführen? Da es eher kleine und wenige GUI-Objekte sind, ist der zusätzliche Speicherbedarf eher klein. Zudem kannst du die Lebensdauer an das GUI-Objekt binden oder sogar an dessen Sichtbarkeit.
Grüssli
PS: Mir kam noch gerade in den Sinn, dass sowas ähnliches letztens bei Boost aktzeptiert wurde. Vielleicht lohnt sich mal ein Blick, wobei ich es mir noch nicht genauer angeschaut habe:
http://www.boost.org/doc/libs/1_44_0/libs/flyweight/doc/index.html
-
Dravere schrieb:
Was aber, wie du inzwischen wohl bemerkt hast, eher von Nachteil ist, weil das eine nichts mit dem anderen zu tun hat. Ich würde dir dringend zur Trennung von den Kriterien raten.
Die angegebenen Kriterien lassen sich zu einem Schlüssel formen (Hash oder im einfachsten Fall ein String). Nicht angegebene Kriterien werden darin nicht berücksichtigt. Insofern sind die Kriterien schon unabhängig voneinander. Oder wie würdest du die Trennung umsetzen?
Dravere schrieb:
Wenn du dies nicht definierst, kann man auch nicht darüber diskutieren, was zurückgegeben werden soll. Schliesslich hängt dies stark davon ab, wie und wann die Freigabe erfolgen soll. Wenn die Freigabe zum Beispiel erst am Ende des Programmes erfolgt, bzw. wenn der Manager zerstört wird, kann man gleich rohe Zeiger zurückgeben.
Wenn man Policies erlaubt, kann man den Freigabezeitpunkt logischerweise nicht allgemein festlegen. Ich habe an eine
shared_ptr-ähnliche Klasse gedacht, die beim Zerstören der letzten Referenz eine (vom Benutzer konfigurierbare) Aktion durchführt, z.B. die Ressource unmittelbar freigibt oder eben eine kompliziertere Logik beinhaltet.Dravere schrieb:
Schlussendlich, nehme ich an, geht es dir darum, dass du die Objekte nicht immer neu zeichnen musst. Da frage ich mich, ob es nicht klüger wäre, diesen Cache pro Objekt einzuführen?
Du meinst pro GUI-Komponente? Dann würde ich aber den Anwendungsbereich der Ressourcenverwaltung auf GUI beschränken, was ich eigentlich nicht will. Und wenn ich nur einen objekt-internen Cache habe, können ja mehrere gleiche GUI-Komponenten ihre Ressourcen gar nicht teilen, oder wie ist das zu verstehen?
Dravere schrieb:
PS: Mir kam noch gerade in den Sinn, dass sowas ähnliches letztens bei Boost aktzeptiert wurde. Vielleicht lohnt sich mal ein Blick, wobei ich es mir noch nicht genauer angeschaut habe:
http://www.boost.org/doc/libs/1_44_0/libs/flyweight/doc/index.htmlAh, das sieht gut aus. Danke, das schau ich mir mal an.
-
Nexus schrieb:
Die angegebenen Kriterien lassen sich zu einem Schlüssel formen (Hash oder im einfachsten Fall ein String). Nicht angegebene Kriterien werden darin nicht berücksichtigt. Insofern sind die Kriterien schon unabhängig voneinander. Oder wie würdest du die Trennung umsetzen?
In dem du sie gar nicht erst in einem Schlüssel vereinst. Es sind unterschiedliche Problemstellungen, welche jeweils eine eigene Lösung benötigen. Der Typ der Ressource ist ein Kriterium und die Grösse ist ein anderes Kriterium. Beides sind eine Art von Schlüssel, welche auf unterschiedliche Container angewandt werden sollten. Steck nicht alles in den gleichen Container und probier dann die Schlüssel irgendwie über zig Umwege zu kombinieren. Du hast ja im Eingangsbeitrag selbst gesagt, wenn du die ID und zusätzliche Eigenschaften in den Schlüssel mischst, dass du dann nicht mehr nur nach der ID suchen kannst. Dass ist eigentlich der beste Grund dafür, dass du das Zeug nicht mischen solltest.
Über das erste Kriterium und dem ersten Container kommst du an einen zweiten Container, worauf du das zweite Kriterium anwenden kannst.Nexus schrieb:
Wenn man Policies erlaubt, kann man den Freigabezeitpunkt logischerweise nicht allgemein festlegen.
Schon klar, aber man muss mindestens definieren, ob man Policies erlaubt. Und in welchem Bereich diese Policies liegen sollen. Man sollte also einen gewissen Rahmen ziehen, sonst kann man das Problem gar nicht erst fassen. Was du danach geschrieben hast, ist schon mal ein grober Rahmen.
Nexus schrieb:
Ich habe an eine
shared_ptr-ähnliche Klasse gedacht, die beim Zerstören der letzten Referenz eine (vom Benutzer konfigurierbare) Aktion durchführt, z.B. die Ressource unmittelbar freigibt oder eben eine kompliziertere Logik beinhaltet.Jetzt wäre es noch sinnvoll diese Aktion besser einzurahmen. Wie kann man die Aktion definieren? Was soll möglich sein? Worauf hat man in dieser Aktion zugriff? Da das Zerstören auch eng mit dem Erstellen zusammenhängt, wäre hier die Frage, ob man mit der Policy am besten gleich beides steuert? Soll es über Templates gehen oder ein Strategy-Pattern zur Laufzeit?
Die Definition hier ist einfach noch viel zu wage.
Nexus schrieb:
Du meinst pro GUI-Komponente? Dann würde ich aber den Anwendungsbereich der Ressourcenverwaltung auf GUI beschränken, was ich eigentlich nicht will.
Die Frage ist, ob du auch wieder mal alles über einen Kamm ziehen willst. Die Ressourcenverwaltung von einem GUI kann nun mal ziemlich anders aussehen als jene von einem Spiel.
Nexus schrieb:
Und wenn ich nur einen objekt-internen Cache habe, können ja mehrere gleiche GUI-Komponenten ihre Ressourcen gar nicht teilen, oder wie ist das zu verstehen?
Richtig, dass wäre der Sinn der Sache. Umso weniger Ressourcen gemeinsam verwendet werden, desto einfacher wird die Logik dazu. Gründsätzlich machst du hier ja Performance-Optimierungen. Mit deiner Lösung probierst du das letzte bisschen an Performance rauszuholen, allerdings nur auf dem Blatt Papier. Dafür machst du eine extrem komplizierte Logik dazu. Dabei wäre dies vielleicht gar nicht nötig. Wenn jede GUI-Komponente sich einmal selber zeichnet und danach den eigenen Cache verwendet, wäre dies vielleicht nicht die schnellste Lösung, aber immer noch absolut ausreichend. Dafür ist aber die Logik dazu deutlich einfacher.
Grüssli
-
Dravere schrieb:
In dem du sie gar nicht erst in einem Schlüssel vereinst.
[...]
Über das erste Kriterium und dem ersten Container kommst du an einen zweiten Container, worauf du das zweite Kriterium anwenden kannst.Du meinst also eine Abbildung der Art
ID -> Grösse -> Ressourcestatt
(ID, Grösse) -> Ressource, implementiert durch verschachtelte Container? Okay, das wäre auch eine Möglichkeit. Nach der ID suchen kann man aber so immer noch nicht eindeutig, da möglicherweise mehrere Grössen damit assoziiert sind.
Dravere schrieb:
Die Definition hier ist einfach noch viel zu wage.
Das mag stimmen, aber bevor ich mich mit den Details befasse, will zuerst ein grobes Konzept fürs Gesamtdesign haben. Es bringt nichts, wenn mir schon eine fertige Implementierung vor Augen schwebt, die sich dann doch nicht in den Rest integrieren lässt.
Dravere schrieb:
Die Frage ist, ob du auch wieder mal alles über einen Kamm ziehen willst. Die Ressourcenverwaltung von einem GUI kann nun mal ziemlich anders aussehen als jene von einem Spiel.
Naja, es gibt einige Gemeinsamkeiten. Statt Schaltflächen handelt es sich vielleicht um Sprites, und es kommen noch zig andere Ressourcen dazu. Ich wollte das einigermassen generisch lösen, um nicht für jeden Anwendungsfall eine neue ResourceManager-Klasse benutzen zu müssen.
Dravere schrieb:
Wenn jede GUI-Komponente sich einmal selber zeichnet und danach den eigenen Cache verwendet, wäre dies vielleicht nicht die schnellste Lösung, aber immer noch absolut ausreichend. Dafür ist aber die Logik dazu deutlich einfacher.
Es kann ja sein, dass das Laufzeitverhalten bei ein paar wenigen Schaltflächen nicht ins Gewicht fällt. Aber bei einem komplexeren Spiel können durchaus viele, teilweise grosse Bilder/Texturen geladen werden – neben Soundeffekten, Musik-Streams, evtl. 3D-Modellen etc. Da lohnt es sich schon, keine Ressourcen zu duplizieren.
Übrigens, bei einer einzelnen GUI-Komponente von Caching zu sprechen, halte ich für übertrieben.

-
Falls es jemanden interessiert, ich habe eine vorläufige Lösung gefunden. Vielleicht stolpert ja jemand über diesen Thread und kann irgendwas davon brauchen. Auf jeden Fall vielen Dank für alle Inspirationen in diesem Thread.

Und zwar habe ich den
ResourceManagerals Template implementiert, sodass er für beliebige Ressourcen verwendet werden kann. Er bietet nun hauptsächlich die MethodenAcquire(),Search()undRelease()wie gehabt an. Um die Ressourcen anzusprechen, benutze ich Key-Klassen. Diese enthalten Ladeinformationen (z.B. Pfad oder Grösse beim Bild) und eine optionalen String-ID. Ich habe dazu zuerst einen Typ-Tag wie bei Boost.Flyweight genommen, jedoch hat es sich als zu wenig flexibel herausgestellt, den Tag zur Kompilierzeit zu kennen.Wenn jemand nun
Acquire()aufruft, erhält er einenResourcePtr. Dabei handelt es sich um einen Smart-Pointer mit zentraler Referenzzählung, ähnlich wie Draveresresource_slot. Zudem werden alleResourcePtr-Instanzen aufNULLgesetzt, wenn die entsprechende Ressource freigegeben wird. Was ich auch noch gemacht habe, ist Const-Correctness zu implementieren, sodassResourcePtr<const Resource>vonResourcePtr<Resource>konstruiert werden kann und ein konstanterResourceManagernur Const-Zeiger zurückgibt.