Salut,

*** Avant toute chose : mettez à jour votre dépôt SubVersion local, j'ai 
changé un paquet de fichiers !!! ***

*** Email un peu long mais c'est pour expliquer en profondeur un problème de 
Wormux vieux de 4 ans au moins ***

J'ai enfin fait le grand pas : j'ai écrit la classe qui va gérer des 
singletons. En deux mots, c'est des instances qui vont être créer lorsqu'on y 
accède. L'avantage est qu'on contrôle la création de l'instance, et qu'on 
peut ajouter des bouts de code (ex: vérifier que SDL est lancé avant de 
charger une image!). D'ailleurs j'ai trouvé une erreur: ImagesParSeconde 
instanciait un objet Text qui a besoin d'une Font qui était créée ... avant 
l'initilisation de SDL et TTF !!! (mais apparement, ça marchait quand 
même :-P).

J'ai donc supprimé toutes les variables globales big_font, small_font, etc. et 
j'ai remplacé ça par : global().small_font() (il faut un #include 
"../include/global.h").

Le désavantage est que ça ajoute deux déréférencement de pointeur et deux if 
pour chaque accès aux variables (un if pour être exact, l'autre est une 
assertion => désactivée en mode non debug). Bon, je pense que ça coûte rien 
du tout au niveau des performances et vous verrez après (dans mon email) tous 
les avantages.

L'autre soucis est que si on met tout ce qui doit être instancié dans 
"Global", la compilation d'un seul fichier demandera l'ensemble des fichiers 
d'entêtes (.h) ... ce qui risque d'être plutôt lourdingue (ralentir la 
compilation quoi). Une solution plus soft serait de faire une classe "Global" 
pour chaque type d'objet (ex: Font, Graphic, Game, ... enfin, il faudrait en 
discuter).

À terme, la majorité des variables globales devraient utiliser des singletons 
(je pense). Par contre, pour certains classes "critiques", c'est peu/pas 
envisageable. Je pense notamment à Config qui est utilisé à peu près partout 
dans le jeu ...

---

Quelques problèmes résolus avec les singletons :

- Le problème des dépendences sont supprimés. Je m'explique : avant, tout 
était en variable globale. Le gros problème est que les objets sont 
instanciés l'un après l'autre. Mais que se passe-t-il lorsque A utilise B 
alors que B n'est pas encore instancié ? Ben le compilateur ne dit rien, et 
souvent ça passe. Mais ça arrive que ça plante ! Exemple : en modifiant 
Config::Config() pour ajouter ttf_filename, j'ai utilisé DEFAULT_DATADIR. 
Mais ... ça plantait lamentablement du côté de std::string !? Après une bonne 
heure j'ai compris le problème : DEFAULT_DATADIR n'existait pas encore ! 
Gloups ! J'espère que vous voyez le problème. J'ai rencontré ce problème très 
très souvent. Un autre au hasard : Time::Time() accédé avant sa création 
(renvoyait n'importe quoi donc).

- On contrôle enfin quand l'objet est détruit. Avant, les bibliothèques 
étaient désactivées (SDL_Quit & Cie, et avant l'équivalent pour ClanLib), et 
seulement après les objets étaient détruits. Le problème est que la 
destruction a besoin de la bibliothèque (ex: l'informer de la destruction de 
l'objet). Et fatalement : ça plante. J'ai eu ce problème très récement : 
Font::~Font() qui appelait TTF_CloseFont() *après* TTF_Quit(). (*bug 
corrigé*)

Hum, il doit y avoir d'autres avantages, mais je ne vois pas trop là tout de 
suite.

J'espère que je vous ai convaincu de l'utilité de mon dernier patch de 1000 
lignes :-P

Victor
-- 
Victor Stinner - étudiant à l'UTBM (Belfort, France)
http://www.haypocalc.com/wiki/Victor_Stinner

Répondre à