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

29.11.2015, 20:12

[C++] Vererbung/Map Problem

Hey zusammen,

bei meinem aktuellsten Projekt habe ich mich mal an eine neue Herangehensweise für meinen Resource-Manager gemacht, doch stoße auf ein Problem, von dem ich nicht recht weiß wie ich es lösen soll.
Nun aber erstmal eine kurze Erklärung zu meiner Struktur: (Immer nur das für die Problemstellung benötigte, der Rest mit "//..." ausgelassen
Mein Resource-Manager speichert alle Resourc-en(momentan Shader, Meshes und Texturen) in folgender Map

C-/C++-Quelltext

1
static std::map<resource_key, ResourceTupel> _resources;


ResourceTupel ist eine simple Struktur zur Verwaltung, resource_key meine Möglichkeit auf die einzelnen Objekte zuzugreifen. Dies ist auch der Wert, den später Objekte erhalten die die Resource nutzen wollen.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
typedef unsigned long resource_key;

//...

struct ResourceTupel { 
    ResourceTupel() : resource(Resource(-1)), fileDir(""), users(-1) {};

    Resource resource;
    std::string fileDir;
    int users;
};


Jede Resource ist folgendermaßen aufgebaut:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class Resource
{
public:
    Resource(GLuint id);

//...

    virtual void Bind() const;

//...

protected:
    GLuint _id;
};

class Shader : public Resource {
public:
    Shader(GLuint id);

//...

    void Bind() const override;

//...
};

class Mesh : public Resource {
public:
    Mesh(GLuint id);

//...

    void Bind() const override;
};

class Texture : public Resource {
public:
    Texture(GLuint id);

//...

    void Bind() const override;
};


Meldet nun ein Nutzer an, eine bestimmte Resource benutzen zu wollen(Momentan Anfrage über Filename), erhält er einen key(Falls die Resource schon geladen wurde zu dieser Resource, sonst zu einer neu geladenen).
Nextkey zeigt immer auf den nächsten, nicht benutzten Index.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
template<class T>
resource_key ResourceManager::UseResource(const std::string& fileDir) {

//...

_resources.insert(std::make_pair(_nextkey, ResourceTupel())); //Neues Objekt wird der Map hinzugefügt

//...

_resources[_nextkey].resource = T(ResourceLoader::LoadResource<T>(fileDir)); //Die eigentliche Resource wird erstellt; "LoadResource" liefert die jeweilige OpenGL-ID

//...
}


Will nun ein Objekt eine Resource binden, holt er sich eine Pointer mithilfe von GetResource, die Methode ResInMap überprüft, ob die Resource bereits geladen wurde.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
Resource* ResourceManager::GetResource(resource_key key) {
    if (ResInMap(key))
        return &_resources[key].resource;
    else
        return &Resource(-1);
}

//Beispielhafter Aufruf von "Bind":
ResourceManager::GetResource(_texture)->Bind();



Hier taucht nun das Problem auf: Denn beim erstellen der Objekte wird noch der jeweilige Konstruktor aufgerufen(in dem Beispiel also von Texture), doch der Aufruf von Bind() führt immer in die Methoden der Überklasse Resource.
Da ich bereits eine Zeit lang nicht mehr programmiert habe, bin ich mir nun unsicher ob das
a) das Grundkonzept so nicht funktionieren kann oder
b) ich einfach einen Fehler gemacht habe

Ich hoffe ihr könnt mir helfen, danke im Vor raus!

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

2

29.11.2015, 22:57

Dein nennt sich Slicing... ;)

Dein ResourceTupel speichert eine Resource als Wert. Die Zuweisung hier

C-/C++-Quelltext

1
_resources[_nextkey].resource = T(ResourceLoader::LoadResource<T>(fileDir));

verwendet den Copy-Assignment-Operator der Resource klasse und kopiert damit das Resource Basisklassensubobjekt aus dem T Objekt raus in deine Map.

Wenn du verschiedene von Resource abgeleitete Objekte im selben Container speichern willst, musst du diese z.B. dynamisch erzeugen und einen Pointer auf die Resource in der Map ablegen. Das macht man natürlich nicht per Hand mit new und mit rohen Zeigern, sondern unbedingt mit std::unique_ptr.

Als generellen Hinweis vielleicht nocht: Statt einer std::map würde ich hier eher eine std::unordered_map verwenden. Eine std::map braucht man eigentlich nur sehr selten, beispielsweise, wenn man die Elemente in der Map zu jedem Zeitpunkt in sortierter Form durchlaufen können will. std::unordered_map ist wesentlich effizienter als std::map.

Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von »dot« (29.11.2015, 23:05)


3

29.11.2015, 23:00

C-/C++-Quelltext

1
return &Resource(-1);


Undefiniertes Verhalten, da du einen Pointer auf den Stack zurückgibst, der aber nach Verlassen der Funktion ungültig wird. Gib einfach nullptr zurück und überprüfe dann nach dem Aufruf, ob der Rückgabewert nullptr ist.

Du erstellst ja auch nur Resource-, aber keine Shader-, Mesh- oder Texture-Objekte. Einfach std::unique_ptr<Resource> verwenden und dann mit new Mesh(...) initialisieren, dann wird auch die Bind-Funktion von Mesh aufgerufen.
Cube Universe
Entdecke fremde Welten auf deiner epischen Reise durchs Universum.

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

4

29.11.2015, 23:01

Einfach std::unique_ptr<Resource> verwenden und dann mit new Mesh(...) initialisieren, [...]

besser mit std::make_unique() ;)

C-/C++-Quelltext

1
return &Resource(-1);


Undefiniertes Verhalten, da du einen Pointer auf den Stack zurückgibst, der aber nach Verlassen der Funktion ungültig wird. Gib einfach nullptr zurück und überprüfe dann nach dem Aufruf, ob der Rückgabewert nullptr ist.

Jaein, er versucht hier die Adresse eines temporären Objektes zu returnen. Dass das überhaupt erst kompiliert liegt an einer absichtilichen Nonkonformität von MSVC; ein standardkonformer C++ Compiler sollte diesen Code gar nicht erst kompilieren, denn der Operand des unären & Operators muss ein lvalue sein, was hier nicht gegeben ist... ;)

Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von »dot« (29.11.2015, 23:49)


5

30.11.2015, 10:30

C-/C++-Quelltext

1
return &Resource(-1);

...muss wohl spät gewesen sein als ich diesen nonsens getippt habe :D

Danke für die Hinweise mit dem Slicing und der Map, werde mich heute direkt dransetzen :thumbsup:

Werbeanzeige