[C++] Templatefunktion und Ressourcenmanager

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Tejio
Establishment
Beiträge: 107
Registriert: 11.11.2010, 11:33

[C++] Templatefunktion und Ressourcenmanager

Beitrag von Tejio »

Hallo alle zusammen,

heute habe ich beim Stöbern im Internet ein Tutorial von (NUITEQ gefunden, indem sich für mich ein interessantes Stück Quellcode befindet. Es handelt sich dabei um eine einzelne Zeile Code, wie ihr seht, und es geht um einen Ressourcenmanager. Die Idee eines solchen Managers ist ansich nicht neu, nur habe ich soetwas noch nicht entwickelt. Um endlich auf den Punkt zu kommen, wie müsste die passende Template-Funktion namens load von ResourceManager aussehen?

Original:

Code: Alles auswählen

...
mpExampleTexture = Blobz::ResourceManager::getInstance().load<Blobz::Texture>("../applications/Tutorial1/data/startPress.png", true);
...
Meine Idee

Code: Alles auswählen

template<class T>
T& toResourceManager::load(const std::string& strFileName)
{
  return T->load(strFileName);
}
Ich hab es mir folgendermaßeng gedacht:

alle Ressourcen erben von der Klasse toResource
toResource beinhaltet eine Funktion namens load zum Laden der Daten
der Ressourcenmanager macht nichts anderes als diese Funktion toResource->load aufzurufen
das Laden der Daten kann die Ressource selbst machen oder einem toResourceLoader überlassen, falls dieser verschiedene Ressourcen unterstützt


Die Aufgabe vom Ressourcenmanager ist es, zum einen das korrekte Laden der Daten zu gewährleisten und zum anderen einen allgemeinen Zugriff auf alle Ressourcen zu gewähren, über eine konsistente Schnittstelle. Das Speichern der Daten würde ich dann über eine Map mit dem vollständigen Dateinamen inklusive Pfad als Index nutzen.

Ich würde mich über Anregungen, Aufregungen und Verbesserungsvorschläge sehr freuen. Ich wünsche euch allen schon mal einen schönen Abend ;)


Gruß, Tejio
Benutzeravatar
Krishty
Establishment
Beiträge: 8267
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Templatefunktion und Ressourcenmanager

Beitrag von Krishty »

Tejio hat geschrieben:

Code: Alles auswählen

template<class T>
T& toResourceManager::load(const std::string& strFileName)
{
  return T->load(strFileName);
}
Müsste es nicht return T::load(strFileName); lauten?
Tejio hat geschrieben:alle Ressourcen erben von der Klasse toResource
toResource beinhaltet eine Funktion namens load zum Laden der Daten
Die Basisklasse ist unnötig – dass die Funktion ein Template ist, bedeutet ja, dass du bereits zur Kompilierzeit weißt, welcher Typ rauskommen wird und dass dieser Typ eine load()-Funktion hat, also kannst du auch direkt eine Instanz dieses Typs zurückgeben.
Tejio hat geschrieben:der Ressourcenmanager macht nichts anderes als diese Funktion toResource->load aufzurufen
das Laden der Daten kann die Ressource selbst machen oder einem toResourceLoader überlassen, falls dieser verschiedene Ressourcen unterstützt
Dann wäre er ja überflüssig, schließlich könntest du mit bedeutend weniger Code manuell das load() der Ressource oder des toResourceLoaders aufrufen. Meiner Einschätzung nach solltest du den Ladevorgang entweder direkt per Spezialisierung in toResourceManager::load<…>(…) oder im toResourceLoader vornehmen und die jeweils andere Klasse als Überfluss verwerfen; dass sich Ressourcen selber laden ist imo nur in den seltensten Fällen (mir fällt bei Spielressourcen keiner ein) sinnvoll.

Gruß, Ky
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
kimmi
Moderator
Beiträge: 1405
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: [C++] Templatefunktion und Ressourcenmanager

Beitrag von kimmi »

Der ResourceManager sollte die bereits geladenen Resourcen kennen und verwalten. Öfter benutzte Resource würden so nur einmal geladen, ansonsten wird die gecachte Variante zurückgegeben.
Die Lösung, das Importieren in der Resource zu implementieren, halte ich auch für etwas unglücklich. Import und Resoruce-Verwaltung an sich sollten ja entkoppelt sein. Hast du die in jeder Resource implementiert, muss jede Resource auch immer das Laden implementieren. Und das riecht für mich nach Redundanzen.
Das Interface toResource würde ich beibehalten, da man Dinge wie Loader-State etc. schliesslich bei jeder Resource benötigt. Aber da kann man sich auch streiten.

Gruß Kimmi
Benutzeravatar
Krishty
Establishment
Beiträge: 8267
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Templatefunktion und Ressourcenmanager

Beitrag von Krishty »

kimmi hat geschrieben:Das Interface toResource würde ich beibehalten, da man Dinge wie Loader-State etc. schliesslich bei jeder Resource benötigt. Aber da kann man sich auch streiten.
Ups, klar. Ich meinte, dass es an dieser Stelle und für diese eine load()-Funktion unnötig ist, extra eine Schnittstelle einzuführen. Im Großen und Ganzen ist es natürlich meist nützlich, wenn alle Ressourcen eine gemeinsame Basis haben.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Tejio
Establishment
Beiträge: 107
Registriert: 11.11.2010, 11:33

Re: [C++] Templatefunktion und Ressourcenmanager

Beitrag von Tejio »

Guten Abend erstmal =)

vielen Dank euch beiden für eure Antworten!! Das Importieren der Daten sollte ansich nur im Zweifelsfall direkt von toResource vorgenommen werden. Regelfall sollte dabei sein, dass das Importieren von einem RessourcenLoader vorgenommen wird. Das war jedenfalls mein Plan^^°
Es wäre also besser, wenn man das Laden einer Ressource implizit über einen Ressourcenlader machen würde und nur die Angabe in der Ressource, ob es geladen wurde, sollte Teil der Schnittstelle von toResource sein? Oder liege ich falsch? Sprich, das Ergebnis besteht aus drei Klassen:

Code: Alles auswählen

/**
 * Von dieser Klasse würden alle Ressourcen erben. 
 */
class toResource
{
  public toResource()
  {
    ...
    toResourceLoader loader;
    loader->load()
    m_bResIsLoaded = true;
    ...
  }
};
/**
 * Von dieser Klasse erben alle Ressourcenlader. 
 */
class toResourceLoader
{
  /* Gibt die unterstützten Ressourcentypen zurück. */
  std::vector<int> getSupportedResourceTypes();
};
/**
 * Diese Klasse ist für das Verwalten aller Ressourcen verantwortlich.
 */
class toResourceManager
{
};

Wundert euch nicht über den Aufbau des Quelltextes, kleine Angewohnheit^^°
Benutzeravatar
Krishty
Establishment
Beiträge: 8267
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Templatefunktion und Ressourcenmanager

Beitrag von Krishty »

Da blicke ich nicht durch, die Ressource lädt sich ja immernoch selber. Ich weiß auch zu wenig über die Ressource-Typen etc um einschätzen zu können, warum Manager und Loader getrennt bleiben müssen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
kimmi
Moderator
Beiträge: 1405
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: [C++] Templatefunktion und Ressourcenmanager

Beitrag von kimmi »

Na ja, ich denke nun beispielsweise an ein 3D-Modell. Wenn man Assimp nehmen würde und das Modell dann als Resource behandelt, macht es durchaus Sinn, Loader und Resource an sich zu trennen. Werbung für eingene Libs ist in diesem Post übrigens reiner Zufall *hüstel* ...

Gruß Kimmi
Benutzeravatar
Krishty
Establishment
Beiträge: 8267
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Templatefunktion und Ressourcenmanager

Beitrag von Krishty »

Nein – dass Ressource und Loader getrennt sein sollen, sage ich ja schon die ganze Zeit :) Ich weiß nur nicht, warum Ressourcemanajah und Ressource-Loader getrennt sein sollen … also, warum der Manager nicht auch das Laden übernehmen soll.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
kimmi
Moderator
Beiträge: 1405
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: [C++] Templatefunktion und Ressourcenmanager

Beitrag von kimmi »

Um Loader beim Manager registrieren zu können beispielsweise. Damit bist du eher erweiterungsfähig, sofern dir das wichtig ist. Ich mach das gern so:
  • Resource ist das Basis-Interface zu der Resource.
  • Resourcemanager verwaltet geladene Resourcen, kann im Hintergrund Resource nachladen, entladen und kümmert sich um das Caching etc.
  • Resourceloader können Resourcen laden und beim Resourcemanager registriert werden.
Gruß Kimmi
Benutzeravatar
Krishty
Establishment
Beiträge: 8267
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Templatefunktion und Ressourcenmanager

Beitrag von Krishty »

Ah, okay. Aber Assimp z.B. liegt schon in einer DLL; d.h. wenn da neue Mesh-Formate hinzukommen, braucht man nur die DLL zu ersetzen und hat seine neuen Loader ohne, dass was an der Engine getan wurde. Für Texturen sollte es auch eine Grafikbibliothek des Vertrauens geben, die ebenfalls per DLL verfügbar ist. Und zur Not kapselt man es selber in einem eigenen Modul und bindet den/die benutzten Loader statisch an dieses.

Wiedemauchsei – noch schnell eine Skizze, wie ich es tun würde:

Code: Alles auswählen

namespace resources {

    struct EType { enum Value { // Workaround, damit C++’ dumme enums nicht das Namespace verpesten
        mesh,
        texture,
        […]
        number // Anzahl der Typen
    }; };

    // Basisklasse für Ressourcen, mit allem, was du in allen Ressourcen brauchst. So eine Schnittstelle ist immer nützlich, wenn man Operationen auf alle Ressourcen generell anwenden will – wären diese Daten direkt im Template, wäre das nur mit relativ viel Geschick und einigem Overhead möglich.
    class IResource {
    protected:

        // Kann nur durch eine abgeleitete Klasse, nämlich eine voll typisierte Ressource, instanziert werden …
        IResource(…);

    };

    // Ressource eines bestimmten Typs, je einzeln durch Template-Spezialisierung definiert.
    template <
        EType itsType
    > class CResource;

    // Weil es bei der Benutzung einfach bequemer ist!
    typedef CResource<EType::mesh> CMesh;
    typedef CResource<EType::texture> CTexture;

    template <
    > class CResource<EType::mesh>
        : public IResource
    {
    private:
        CArray<CVertex> * myVertices; // oder vllt sogar eine aiScene?
        …
    public:

        // Der Konstruktor nimmt nur *fertige* Mesh-Daten möglichst roh entgegen und legt sie in der Instanz zusammen. Hier wird nichts geladen, weil es für die Implementierung eines Meshs vollkommen unwichtig ist, was für Dateiformate es da draußen gibt und wie sie aufgebaut sind. Das macht später der Manager.
        CResource(
            size_t const numberOfVertices,
            CVertex const * const itsVertices,
            …
        )
            : myVertices(numberOfVertices, itsVertices)
            , …
        { };

    };

    template <
    > class CResource<EType::texture>
        : public IResource
    {
    private:
        ilImage myPixels;
        …
    public:

        // Dasselbe wie beim Mesh.
        CResource(
            byte const * const itsPixels
            …
        )
            : myPixels(itsPixels)
            , …
        { };

    };


    class CManager {
    private:
        // Wie du die Ressourcen intern speicherst, musst du dir selber überlegen; hängt von deinem Einsatzzweck ab. Einige schwören auf heterogene oder auf assoziative Container; ich persönlich nutze bloß ein homogenes Array von Containern – einer für jeden Ressource-Typ – weil ich immer über alle Ressourcen desselben Typs iteriere und nie einzelne Ressourcen herauspicken muss. Hier nehmen wir mal was ::std::map-mäßiges, um Ressourcen mit ihren Namen adressieren zu können.
        CMap<CString, IResource> myResourcesPerType[EType::number];

        // Genau wie die Ressource-Klasse definieren wir diese Funktion später per Template-Spezialisierung.
        template <
            EType::EValue itsType
        > CResource<itsType> loadResource(
            CString itsName
        );

    public:

        template <
            EType::EValue itsType
        > CResource<itsType> & getResource(
            CString itsName
        ) {
            // Wenn die Ressource-Klasse kein Template wäre, würde das jetzt viele hässliche switch-Statements oder Schleifen bedeuten, weil wir je nach Ressource-Typ eine Funktion loadMesh(), loadTexture() etc aufrufen müssten. Yippie!

            // Checken, ob die Ressource schon geladen ist:
            if(IResource * const toResult = myResourcesPerType[itsType].find(itsName)) { // Variable in if-Statement deklarieren bedeutet, eine Zeile zu sparen. Instant flamewar!
                // Sicherstellen, dass wir im vorherigen Programmverlauf auch tatsächlich nur passend typisierte Ressourcen in dem Array gespeichert haben. Mit heterogenen Containern wäre die ganze Casterei überflüssig, aber ich habe gerade keinen zur Hand.
                assert(nullptr != dynamic_cast<CResource<itsType> *>(toResult));
                return *dynamic_cast<CResource<itsType> *>(toResult);
            }

            // Ressource ist noch nicht geladen. Auf, auf!
            else {
                CResource<itsType> & result = myResourcesPerType[itsType].insert(itsName, loadResource<itsType>(itsName));
                return result;
            }
        }

    }; // class CManager

    template <
    > CMesh CManager::loadResource<EType::model>(
        CString itsName
    ) {
        aiScene * toImportedScene = aiLoad("models//" + itsName); // Das fällt natürlich am Ende noch ein wenig komplexer aus, mit Caches oder sonstwas. Außerdem unterstützt die Board-Software keine Backslashes.
        return CMesh(toImportedScene->Mesh->NumVertices, toImportedScene->Mesh->Vertices, …);
    }

    template <
    > CTexture CManager::loadResource<EType::texture>(
        CString itsName
    ) {
        CImage textureData = MyImageLib::load("textures//" + itsName);
        return CTexture(textureData->rawPixels, …);
    }

} // namespace resources

resources::CManager manajah;
auto tex = manajah.load<resources::EType::texture>("Blütenzauber");
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
kimmi
Moderator
Beiträge: 1405
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: [C++] Templatefunktion und Ressourcenmanager

Beitrag von kimmi »

Ein Nachteil sehe ich gerade: Wenn ein Plugin eigene resourcen anmelden will, müsste sie sich doch per Id anmelden, richtig? Und das ist bei einem Enum schwierig. Oder habe ich etwas übersehen?

Viele liebe Grüße,
Kimmi
Benutzeravatar
Krishty
Establishment
Beiträge: 8267
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Templatefunktion und Ressourcenmanager

Beitrag von Krishty »

kimmi hat geschrieben:Ein Nachteil sehe ich gerade: Wenn ein Plugin eigene resourcen anmelden will, müsste sie sich doch per Id anmelden, richtig? Und das ist bei einem Enum schwierig. Oder habe ich etwas übersehen?
Nein, stimmt schon. Bei mir habe ich das dadurch gelöst, dass Ressourcen möglichst generell & schmal gehalten sind und Plugins private Daten anhängen können, auf die das restliche Programm keinen Zugriff hat. Imo ist die Schnittmenge der Daten, die Plugins untereinander zu teilen haben, eh winzig … darum kocht dann jedes Plugin sein eigenes Süppchen mit hochspezialisierten Datensätzen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Antworten