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

xardias

Community-Fossil

Beiträge: 2 731

Wohnort: Santa Clara, CA

Beruf: Software Engineer

  • Private Nachricht senden

21

26.07.2010, 22:04


Aber ja, es dauert etwas, bis man das eingesehen hat.

Da war ich schonmal, nur mitlerweile denke ich da etwas pragmatischer ;)

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

22

26.07.2010, 22:21

Man muss sich nur vorher Gedanken machen. Dann ist gegen ein Singleton nichts einzuwenden.
Wie gesagt, da stimme ich Dir zu. Ich habe auch in meinem aktuellen Framework eine *Art* Singleton... zumindest eine Klasse, die eine Static-Liste von Kostruktoren als Fabrik hält, die sie mittels Refactoring über die Assembly gewonnen hat. Das braucht man nur einmal global, egal wie viele Plugins ich dazu lade oder wie viele Ressourcen-Manager oder sonstewas verwendet werden. Aber, wie auch schon gesagt, werden Singletons gerade in Hobby-Projekten gerne für Dinge verwendet, wo es fraglich ist, bzw. wo nur aus Faulheit ein Singleton gewählt wurde. Z.B. will *ich* nicht nur einen Ressourcen-Manager haben können, denn dann hätte ich bei der Verwaltung von Plugin-Daten oder Multiplen Fenstern arge Probleme, wenn eines davon sich dazu entscheidet alle geladenen Ressourcen zu entladen... oder eines will Daten eben *nicht* aus dem Manager-Cache laden, sondern von Platte (was bei Singletons mit Verschachtelter Verwendung durch Ressourcen-Unterklassen unmöglich ist) oder ähnliches. Man sollte also schon wirklich wissen was man tut, das ist aber leider viel zu selten der Fall, ganz ehrlich. Ist nicht böse gemeint, auch ich erwische mich immer wieder dabei.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

SilentWriter

Frischling

Beiträge: 13

Wohnort: Berlin

  • Private Nachricht senden

23

26.07.2010, 23:42

Ich finde keine der beiden Methoden praktisch "sauber". Das Problem: Es ist praktisch einfach eine Sauerei in einem Konstruktor eine Exception im Fehlerfall zu werfen, wenn etwas schief läuft. Und die nachträgliche Prüfung ist auch nicht so der Hit. Und die zweite herangehensweise ist viel zu unflexibel. Und das mit den static Variablen solltest du sehr schnell vergessen. Als ich das mal früher so gemacht habe, gab es einfach nur Ärger damit.

Ich empfehle eher eine Templateklasse zu verwenden, die eine Instanz-Variable enthält (ID3D11Device*, ID3D11Context*, ID3D11Buffer*, ...). Klassen, die das Device oder ein Context benötigen, sollten diese in einer create()-Methode bekommen.

Folgendes funktionelles Beispiel meiner Engine:

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
////////////////////////////////////////////////////////////////////////////
/// \brief  A RAII (Resource Acquisition Is Initialization) class with
///         a data member (m_instance).
///
///         If RAII is enabled, the derived class should delete its
///         data content.
////////////////////////////////////////////////////////////////////////////

template<typename T> class bsxRAII
{
protected:
    //! If enabled, a derived class should release itself its data per deconstructor.
    bool m_activeRAII;

    //! The instance of data.
    T    m_instance;

public:

    ////////////////////////////////////////////////////////////////////////////
    /// \brief  Default constructor. 
    ////////////////////////////////////////////////////////////////////////////

    __forceinline bsxRAII()
    {
        m_activeRAII = true;
    }

    ////////////////////////////////////////////////////////////////////////////
    /// \brief  Constructor.
    ///         Note: It disables the RAII mode, because it is using
    ///               an external resource.
    ///
    /// \param  from    The RAII class.
    ////////////////////////////////////////////////////////////////////////////

    __forceinline bsxRAII(const bsxRAII<T> &from)
    {
        setInstance(from.getInstance());
    }

    ////////////////////////////////////////////////////////////////////////////
    /// \brief  The copy operator for the class. 
    ///         Note: It disables the RAII mode, because it is using
    ///               an external resource.
    ///
    /// \param  from    The RAII class.
    ///
    /// \return A shallow copy of this object. 
    ////////////////////////////////////////////////////////////////////////////

    __forceinline bsxRAII operator=(const bsxRAII<T> &from)
    {
        setInstance(from.getInstance());
        return *this;
    }

    ////////////////////////////////////////////////////////////////////////////
    /// \brief  Gets the instance. 
    ///
    /// \return The instance. 
    ////////////////////////////////////////////////////////////////////////////

    __forceinline T getInstance() const
    {
        return m_instance;
    }

    ////////////////////////////////////////////////////////////////////////////
    /// \brief  Sets the instance of the class. 
    ///         Note: It disables the RAII mode, because it is using
    ///               an external resource.
    ///
    /// \param  instance    The instance. 
    ////////////////////////////////////////////////////////////////////////////

    __forceinline void setInstance(const T &instance)
    {
        m_instance = instance;
        disableRAII();
    }

    ////////////////////////////////////////////////////////////////////////////
    /// \brief  Enables the RAII mode.
    ////////////////////////////////////////////////////////////////////////////

    __forceinline void enableRAII()
    {
        m_activeRAII = true;
    }

    ////////////////////////////////////////////////////////////////////////////
    /// \brief  Disables the RAII mode. 
    ////////////////////////////////////////////////////////////////////////////

    __forceinline void disableRAII()
    {
        m_activeRAII = false;
    }

    ////////////////////////////////////////////////////////////////////////////
    /// \brief  Query if this object is a RAII class. 
    ///
    /// \return true if it is an RAII enabled class.
    ////////////////////////////////////////////////////////////////////////////

    __forceinline bool isRAII() const
    {
        return m_activeRAII;
    }

    ////////////////////////////////////////////////////////////////////////////
    /// \brief  The derived class has to implement a release() method.
    ////////////////////////////////////////////////////////////////////////////

    virtual void release() = 0;
};

Vorteile:
bsxRAII: Die wichtige Komponente der Klasse ist klar. Kein dauerndes neues schreiben von "SetDevice(), GetContext(), ...". Klassen, die von dieser ableiten, können RAII benutzen (automatische Ressourcen(de)allokation).

Anwendung (funktionell, wieder ein Beispiel):

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class VertexBuffer : public bsxRAII<ID3D11Buffer*>
{
protected:
    Device  m_Device;
public:
    VertexBuffer() { SafeNull(m_instance); }

    ~VertexBuffer() { if(isRAII()) release(); }

    void create(const Device &iDevice, const void* iData, size_t iSizePerElement, size_t CountOfVertices) { ... }

    ...

    void release() { ... }
}

Was hälst du denn von diesem Ansatz ;) ?

24

26.07.2010, 23:52

Vielen Dank für die vielen Antworten :) Ich werde es mir morgen mal genauer anschauen und versuche es dann mal, vllt schaff ich es auch ein kleines Beispielprogramm zu schreiben ^^

Mfg Male

idontknow

unregistriert

25

27.07.2010, 07:23

@SilentWriter: Ich versteh den Sinn deiner Klasse nicht. Außer dass ich jetzt einheitlich get- und setInstance() aufrufen kann....

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

26

27.07.2010, 08:15

Ich halte das auch für ... hmm naja. Die Lösung das Device auf ein Interface zu abstrahieren finde ich wesentlich besser.
Damit ist der Code überall identisch, bis auf die Stelle, wo man eine Instanz eines speziellen Devices erzeugt. Andernfalls muss ja wirklich ALLES neu kompiliert werden und wenn man mehrere verschiedene Implementierungen verwendet und man hat den Code immens aufgebläht, weil jedes Template vom Compiler in eigenständigen Code umgewandelt wird.
Finde ich an dieser Stelle eher sinnlos.
Mal davon ab, was oder wer hält mich ab vom folgenden Versuch?

C-/C++-Quelltext

1
2
3
class VertexBuffer : public bsxRAII<3DVector*>
{
}

Das geht nach deiner Definition prima, macht aber semantisch keinen Sinn. Da fand ich den Vorschlag das Device als Interface zu machen und dann verschiedene Implementierungen abzuleiten wesentlich eleganter und sinnvoller. Typische Bridge-Konstruktion halt.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

idontknow

unregistriert

27

27.07.2010, 11:26

Warum sollte man mehrere versch. Device Klassen haben? Hab mal den Thread durchgelesen aber kann dem aktuellen Problem leider nicht ganz folgen, könnte mich freundlicherweise wer aufklären? :)

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

28

27.07.2010, 11:41

Weil man DX9, DX10, DX11 oder OpenGL2.X, OpenGL3.X verwenden wollen könnte.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

idontknow

unregistriert

29

27.07.2010, 11:53

najaaaa, so einfach wird das nicht sein denke ich. weil es doch entscheidnede veränderungen gibt, z.b. (hab nur davon gehört) dass alle renderstates in sonem state block gespeichert werden und man den dann setzen kann.

solche sachen die sich doch "sehr" unterscheiden muss man eben irgendwie "aufdrößeln und kann dann sein interface schreiben, dass man eben für jede beliebige Grafik API Version kompilieren kann. Wäre zumindest mein Ansatz.

30

27.07.2010, 13:30

najaaaa, so einfach wird das nicht sein denke ich. weil es doch entscheidnede veränderungen gibt, z.b. (hab nur davon gehört) dass alle renderstates in sonem state block gespeichert werden und man den dann setzen kann.

Es ist auch nicht einfach, aber durchaus möglich.
Bei Ogre z.B. hab ich jetz in der neuen Version DX9, DX10, DX11 und OpenGL renderer gesehen (auch wenn das SDK nur DX9 und OGL hat :( ). Allerdings hab ich da auch noch keine RenderStates in dem sinn gesehen, das wird über die MaterialScripts abstrahiert (kA, wie das dann intern verarbeitet wird)

Werbeanzeige