Bonjour,
J'ai un petit problème d'utilisation des smart pointers de boost, notamment shared_ptr et weak_ptr. Je précise que c'est ma première "vraie" utilisation des smarts pointers (mes expériences précédentes se limitaient à des tests pour voir un peu comment ça marchait).
J'essaie de construire une architecture de type DEM (delegate event model), avec quelque chose de très basique pour commencer. Un peu du style :
Un Listener peut s'enregistrer auprès d'une Source en appelant addListener() sur celle-ci, et se désenregistrer par removeListener(). La source peut ensuite notifier ses listeners_ quand bon lui semble en appelant leur méthode onEvent(). J'omets volontairement toute notion de type d'évènement, identité de la source, etc...
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 class Listener { public: virtual ~Listener() {} virtual void onEvent() = 0; }; class Source { public: virtual ~Source() {} void addListener(Listener*); void removeListener(Listener*); private: list<Listener*> listeners_; };
Mon problème concerne le fait que lorsqu'un Listener est détruit, il faut bien qu'il soit retiré des listeners_ des différentes Sources auprès desquelles il s'est enregistré. On pourrait penser à un système plus complexe où les Sources écoutent elles-mêmes des évènement du genre DeletionEvent envoyés par leur listeners, mais c'est tordu. Un collègue m'a expliqué qu'en Java ce mécanisme s'obtient typiquement à base de weak references. Je me suis donc dit qu'il suffisait d'utiliser des shared_ptr dans mon programme, et de les passer aux Sources au lieu des pointeurs natifs, pour qu'elles puissent en faire des weak_ptr, afin de pouvoir les virer quand leur expired() retourne false (il me semble que c'est comme ça qu'on utilise les weak_ptr, dites-moi si je me trompe).
Du coup la déclaration devient :
Et la méthode addListener() ressemblerait en gros à ceci :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 class Source { public: virtual ~Source() {} void addListener(shared_ptr<Listener>); void removeListener(shared_ptr<Listener>); private: list<weak_ptr<Listener> > listeners_; };
Et la méthode qui notifie les listeners_ en les parcourant se chargerait de tester leur expired() et de les supprimer si besoin.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 void Source::addListener(shared_ptr<Listener> listener) { listeners_.push_back(weak_ptr<Listener>(listener)); }
Sa me semble tenir la route. Seulement voilà mon problème : autant le fait d'attacher un listener à une source sera très facile pour un code client qui fait tout par shared_ptr, et aura donc juste à passer ses shared_ptr de listeners à ses sources, autant c'est beaucoup plus difficile si on veut qu'un listener puisse s'enregistrer tout seul. Vu que quand on est dans le code du listener lui-même, on ne peut bien sûr pas faire :
et encore moins :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 source.addListener(this); // ne compile pas
Du coup je me retrouve avec des belles idées mais qui sont finalement assez inutile puisqu'il est quand même plus que fréquent dans ce type d'architecture qu'une classe dérivant de Listener décide d'elle-même d'écouter une source ! (c'est presque le cas nominal...).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 source.addListener(shared_ptr<Listener>(this)); // provoque une catastrophe : this sera détuit à la fin de l'instruction
Que pensez-vous de ce problème ? Avez-vous déjà été confronté à ce genre de chose ? Je ne suis un habitué ni des smart pointers ni des event models, donc j'avoue être un peu désemparé. J'ai beau être fan de C++, là je dois avouer que ça serait un jeu d'enfant en Java....
Toute idée sera appréciée.
Merci d'avance
Partager