Gestion de ressource RAII

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Salut, j'ai lu le tuto sur le RAII, et j'aimerai donc l'appliquer à mon code. Cependant je ne sais pas si ce que j'ai fait est pertinent..

Engine.h

 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
/*
-Engine.h-
Classe du moteur permettant au jeu de fonctionner. //TODO
*/

#ifndef ENGINE
#define ENGINE

#pragma region Include
#include "StateManager.h"
#include "GameplayState.h"
#include "TextureManager.h"
#include "Exception.h"
#include <SFML/Graphics.hpp>
#pragma endregion

class Engine {
  int m_LastExceptionCode;
  sf::RenderWindow* m_Window;
  TextureManager m_TextureManager;
  Exception m_EHandler;
  GameTime m_GameTime;
  StateManager m_StateManager;
public:
  Engine::Engine();
  Engine::~Engine();
  void run();
  void init();
  void setLastExceptionCode(const int exceptionCode){m_LastExceptionCode = exceptionCode; };
  void exit_now();
  int const getLastExceptionCode(){return m_LastExceptionCode;};
  void write_error(const char*, const char*);
};
#endif

Engine.cpp

 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
#include "Engine.h"
#include <cmath>

Engine::Engine()
{

}

void Engine::init()
{
  setLastExceptionCode(0);

  m_EHandler.bind_engine(this);

  m_Window = new sf::RenderWindow(sf::VideoMode(800, 608), "Eugola");

  //m_Window->setFramerateLimit(60);

  m_TextureManager.bind(&m_EHandler);

  m_StateManager.bind(m_Window, &m_TextureManager, &m_EHandler);

  State *g(0);

  g = new GameplayState();

  m_StateManager.add_state(g);
}
Engine::~Engine()
{
  delete m_Window;
}

Engine.cpp modifié

 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
Engine::Engine()
{
  try {
      setLastExceptionCode(0);

      m_EHandler.bind_engine(this);

      m_Window = new sf::RenderWindow(sf::VideoMode(800, 608), "Eugola");

      //m_Window->setFramerateLimit(60);

      m_TextureManager.bind(&m_EHandler);

      m_StateManager.bind(m_Window, &m_TextureManager, &m_EHandler);
      
      State *g(0);

      g = new GameplayState();

      m_StateManager.add_state(g);
  }
  
  catch (std::exception const& e)
  {
      write_error(typeid(e).name(), e.what());
      MessageBoxA(NULL,
          LPCTSTR(std::string(std::string(typeid(e).name()) + std::string(" exception occured. Could not load the game.\n\n\nPlease send your log to the developer at guillaume.thivolet@gmail.com")).c_str()),
          "Eugola catched an exception",
          MB_OK | MB_ICONERROR);
  }
}

Engine::~Engine()
{
  delete m_Window;
}

main.cpp normal:

 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
#include "main.h"

using namespace std;

int main(int argc, char* argv[])
{
  Engine engine;

  try 
  {
      engine.init();
      try
      {
          engine.run();
      }

      catch (exception const& e)
      {
          engine.write_error(typeid(e).name(), e.what());
          MessageBoxA(NULL,
              LPCTSTR(string(string(typeid(e).name()) + string(" exception occured.\n\n\nPlease send your log to the developer at guillaume.thivolet@gmail.com")).c_str()),
              "Eugola catched an exception",
              MB_OK | MB_ICONERROR);
      }
  }
  catch(exception const& e)
  {
      engine.write_error(typeid(e).name(), e.what());
      MessageBoxA(NULL,
          LPCTSTR(string(string(typeid(e).name()) + string(" exception occured. Could not load the game.\n\n\nPlease send your log to the developer at guillaume.thivolet@gmail.com")).c_str()),
          "Eugola catched an exception",
          MB_OK | MB_ICONERROR);
  }
    return engine.getLastExceptionCode(); //0 by default
}

main.cpp modifié:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include "main.h"

using namespace std;

int main(int argc, char* argv[])
{
  Engine engine;

  try 
  {

          engine.run();
      
  }
  catch (exception const& e)
  {
      engine.write_error(typeid(e).name(), e.what());
      MessageBoxA(NULL,
          LPCTSTR(string(string(typeid(e).name()) + string(" exception occured.\n\n\nPlease send your log to the developer at guillaume.thivolet@gmail.com")).c_str()),
          "Eugola catched an exception",
          MB_OK | MB_ICONERROR);
  }
    return engine.getLastExceptionCode(); //0 by default
}

Est-ce comme ça que l'on applique ce principe ?

Édité par Unknown

Vive la science

+0 -0

Cette réponse a aidé l'auteur du sujet

Lu'!

Pourquoi tu te fais chier avec une fonction "init" dans Engine ? Le rôle d'initialisation, c'est le constructeur qui l'assure. Et du coup, tu n'as pas besoin de pointeur pour la RenderWindow, tu as juste à faire une instanciation directe.

Et pour l'état, si StateManager en prend la responsabilité, le truc cohérent c'est de donner un unique_ptr à la fonction "add_state" qui fonctionnera comme un puit. (En l'occurrence, dans ton code actuel, si elle échoue c'est techniquement une instanciation paumée).

Pour ce qui est de l'usage de "typeid", il n'est pas très utile. Si tu pouvais t'en débarrasser et jeter le RTTI au passage, ce serait que bénéfique pour ta conception et tes performances.

Édité par Ksass`Peuk

First : Always RTFM - "Tout devrait être rendu aussi simple que possible, mais pas plus." A.Einstein

+1 -0

Cette réponse a aidé l'auteur du sujet

Je me souviens plus du contenu de l'article sur RAII ici, du coup, j'en profite pour faire de la pub pour mon article :) http://guillaume.belz.free.fr/doku.php?id=pourquoi_le_raii_est_fondamental_en_c

Pour commencer, le try-catch sert à récupérer une exception et faire quelque chose (libérer les ressources, remettre le système dans un état stable valide, etc). Juste afficher un message d'erreur, c'est pas réellement "faire quelque chose" (par exemple dans ton code, m_Window ne sera forcement détruite, la texture pas forcement release, le StateManager peut contenir des états qui ne servent à rien, etc).

Pour le RAII, je pense que tu n'as pas compris le principe et l'intérêt. Cela permet d'automatiser et sécuriser la libération de ressources. Par exemple, tu as une fenêtre qui est allouée et qui doit être libérée. Il faudrait écrire le code suivant pour gérer correctement cette ressource :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Engine::Engine() {
    ... // du code...
    try {
        m_Window = new sf::RenderWindow(sf::VideoMode(800, 608), "Eugola");
        ... // du code...
    } catch (...) { // nonnnn !!!
        delete m_Window;
    }
}

Engine::~Engine() {
    delete m_Window;
}

Le try-catch n'a qu'une seule fonction ici : libérer la ressource (la fenêtre). Et surtout, il faut créer des blocs try-catch pour chaque ressource à libérer, ce qui peut vite devenir lourd (cf les codes d'exemple donnés dans l'article "Retour de fonctions ou exceptions ?").

Le RAII utilise une propriété du destructeur : celui-ci est toujours appelé (si l'objet est correctement construit, c'est à dire que le constructeur à fini). Du coup, pour gérer une ressource, il suffit de l'acquérir dans le constructeur et la libérer dans le destructeur :

1
2
3
4
5
6
7
class WindowPointer {
public:
    WindowPointer() : m_Window(new sf::RenderWindow(sf::VideoMode(800, 608), "Eugola")) {}
    ~WindowPointer() { delete m_Window; }
private:
    sf::RenderWindow* m_Window{};
};

Ce simple code garantie que la fenêtre est toujours libérée correctement (si le constructeur plante, la fenêtre n'est pas construite et il n'y a pas besoin d'appeler delete. Si le constructeur ne plante pas, delete sera toujours appelé). Pas besoin de try-catch.

Et ce principe est applicable à n'importe quoi qui a besoin d'être libéré (libérer une ressource avec release, libérer un mutex avec unlock, fermer un fichier, clore une connexion réseau, etc). Note : la libération de la mémoire est quelque chose de tellement courant qu'il existe beaucoup de classes de la STL qui font cela, selon les besoins (unique_ptr, shared_ptr, vector, string, etc). Donc pas besoin d'écrire une classe "WindowPointer" (dans ton cas, un std::unique_ptr correspond à ce que tu veux faire).

Édité par gbdivers

+1 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte