Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

17.07.2011, 14:33

Gemeinsamer Ressourcenzugriff über Smart-Pointer

Hallo zusammen,

Ich habe eine Klasse, die ein Cache für Ressourcen darstellt. D.h. wenn der Benutzer eine Ressource anfordert, wird geprüft, ob sie schon mal geladen wurde, und in dem Fall zurückgegeben. Ansonsten wird sie automatisch geladen.

Nun überlege ich mir, wie der Zugriff aussehen soll. Momentan verwende ich ein eigenes Smart-Pointer-Klassentemplate ResourcePtr<T>. Der Vorteil dabei ist, dass ich z.B. tracken kann, wer die Ressource gerade verwendet (für Debugging, oder um Freigabe zu erzwingen). Allerdings überlege ich mir, auf shared_ptr<T> umzusteigen, weil es sich hierbei um einen Standard-Smart-Pointer handelt, den jeder kennt. Ausserdem kann dann die Ressource ohne Abhängigkeit zu meinem Ressourcen-Cache weitergereicht werden. Auf der anderen Seite würde ich etwas Flexibilität verlieren und hätte keine Möglichkeit mehr, später allfällige Features zur Smart-Pointer-Klasse hinzuzufügen.

Der Smart-Pointer würde so oder so Custom-Deleter anbieten, damit man auch normale Objekte reinpacken kann, die nicht vom Cache geladen werden. Damit könnte man sogar Null-Deleter verwenden und z.B. Stack-Objekte referenzieren, ohne dass diese zerstört werden.

Was findet ihr aus Anwendersicht sinnvoller? Spezialisierter ResourcePtr oder allgemeingültiger shared_ptr?

Und kennt ihr Bibliotheken, die die eine oder andere Möglichkeit verwenden? Ich habe bisher vor allem Ogre gefunden, wo ein spezifischer SharedPtr verwendet wird.

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

2

17.07.2011, 15:44

Um was für eine Art Ressource handelt es sich da genau?

3

17.07.2011, 15:48

Primär SFML-Ressourcen wie Bilder, Schriftarten, Musik, etc. Der Cache ist als Template realisiert, T steht dabei für die entsprechende Ressourcenklasse (daher auch z.B. shared_ptr<T> für die Rückgabe).

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

4

17.07.2011, 15:51

Kann sich dein Ressourcenmanager nicht drum kümmern am Ende alles freizugeben? Dann kannst du einfach einen normalen Pointer returnen und dir sowas wie Reference-Counting gänzlich sparen. Ansonsten könntest du dir überlegen deine Ressourcen in ein entsprechendes Objekt zu packen und nicht einfach direkt Pointer auf das rohe Objekt herumzureichen.

5

17.07.2011, 16:28

Meinst du, es wäre besser, wenn der Ressourcenmanager alle Ressourcen besässe? Dann müsste der Benutzer jeweils sicherstellen können, dass er länger lebt als die Ressourcen. Das wäre auch eine Möglichkeit, momentan können Ressourcen unabhängig von ihrem Erzeuger weiterleben. Sowas wie ein Singleton möchte ich aber nicht erzwingen.

Das "in ein Objekt packen" läuft doch auf eine Art Smart-Pointer hinaus, oder?

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

6

17.07.2011, 16:36

Das "in ein Objekt packen" läuft doch auf eine Art Smart-Pointer hinaus, oder?

Nicht unbedingt. Ich denke da an Objekte die statt direktem Zugriff auf die Daten ein Interface dafür bieten was du mit den Daten so anstellen willst. Abgesehen davon könntest du einen allfälligen Reference-Counter in ein solches Objekt direkt einbauen.

Meinst du, es wäre besser, wenn der Ressourcenmanager alle Ressourcen besässe? Dann müsste der Benutzer jeweils sicherstellen können, dass er länger lebt als die Ressourcen. Sowas wie ein Singleton möchte ich aber nicht erzwingen.

Nicht alle, nur die die er verwaltet. Ist eben die Frage ob es notwendig wird bestimmte Ressourcen zur Laufzeit wieder freizugeben. Unabhängig davon braucht man in keinem Fall ein Singleton ;)

7

17.07.2011, 16:47

Nicht unbedingt. Ich denke da an Objekte die statt direktem Zugriff auf die Daten ein Interface dafür bieten was du mit den Daten so anstellen willst.
Das geht in meinem Fall nur begrenzt, da viele Klassen Vollzugriff benötigen (z.B. muss ein sf::Sprite auf ein sf::Image verweisen).

Nicht alle, nur die die er verwaltet. Ist eben die Frage ob es notwendig wird bestimmte Ressourcen zur Laufzeit wieder freizugeben.
Die Möglichkeit zur Freigabe möchte ich schon haben, allerdings tendiere ich dazu, dass nur unbenutzte Ressourcen freigegeben können. Für eine erzwungene Freigabe könnte ich shared_ptr ohnehin nicht nehmen.

Das Problem bei rohen Zeigern ist, dass ich nicht kontrollieren kann, ob eine Ressource noch benutzt wird. Strategien wie "gebe frei sobald unbenutzt" funktionieren also nicht mehr, der Benutzer muss alles explizit befehlen (oder auf den Destruktor warten). Zusätzlich kann es zu Dangling-Pointers kommen, falls aus Versehen zu früh freigegeben wird. Von daher sind Smart-Pointer schon praktisch. Ich denke auch, der Overhead des Reference-Counters und allfälliger Synchronisierungen ist akzeptabel.

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

8

17.07.2011, 17:03

Das geht in meinem Fall nur begrenzt, da viele Klassen Vollzugriff benötigen (z.B. muss ein sf::Sprite auf ein sf::Image verweisen).

Nur ein kleines Beispiel weil ichs grad gestern so gemacht hab: Wir haben Shadercode als Ressource und wollen daraus Shader erzeugen. Anstatt nun den rohen Shadercode rumzureichen geben wir unserer Klasse ShaderBinary einfach eine Methode createShader() die einen Shader erzeugen kann. So bleiben die Daten immer in unserem Ressourcenobjekt und wir können trotzdem einen Shader erzeugen, ganz ohne die Bytes dahinter jemals zu Gesicht zu bekommen. Das ist natürlich nur eine mögliche Strategie die sicher nicht in jedem Fall zielführend ist, aber vielleicht inspiriert es dich ja.

Nicht alle, nur die die er verwaltet. Ist eben die Frage ob es notwendig wird bestimmte Ressourcen zur Laufzeit wieder freizugeben.
Die Möglichkeit zur Freigabe möchte ich schon haben, allerdings tendiere ich dazu, dass nur unbenutzte Ressourcen freigegeben können.

Naja, in so einem Fall wäre Reference-Counting natürlich eine praktikable Lösung.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »dot« (17.07.2011, 17:09)


9

18.07.2011, 09:38

Ok, vielen Dank für deine bisherigen Antworten! :)

Naja, in so einem Fall wäre Reference-Counting natürlich eine praktikable Lösung.
Was würdest du aus User-Sicht vorziehen, einen std::tr1::shared_ptr oder einen spezifischen Smart-Pointer? Oder spielt das keine Rolle?

Was meinen andere Leute dazu?

TrommlBomml

Community-Fossil

Beiträge: 2 117

Wohnort: Berlin

Beruf: Software-Entwickler

  • Private Nachricht senden

10

18.07.2011, 12:08

Naja von der semantik klingt shared_ptr nach dem richtigen. du teilst eine resource über mehrere instanzen. spezielle logik sollte wohl nicht vonnöten sein ausser das bereits angesprochene reference-counting. wenn du objekte anlegst mit verweis auf resource, count erhöhen, beim klonen von objekten count erhöhen, beim freigeben erniedrigen.

aus benutzersicht: wenn ich eine bibliothek mit automatischem resourcenmanagement habe, möchte ich eigentlich gar nicht wissen, dass es ein shared oder smart-pointer ist. mich interessiert dann höchstens, dass es möglichst einfach ist, resourcen anzufordern, wieder abzugeben und sichergestellt ist, dass ich mir keine sorgen über speicherleichen machen muss.

Werbeanzeige