J'ai un petit problème de design avec un singleton dans une dll.
En effet il se retrouve instancié plusieurs fois alors que je n'ai qu'une seule instance de la dll.
J'ai pu trouver un workaround au problème (utilisation de globals, passage de l'instance en paramètre) mais je ne trouve pas joli ce design.
Dans le cadre du travail, je développe un soft générique qui utilise une dll qui contient les règles custom du client.
Voici un résumé (voire une caricature) du fonctionnement du bazar:
Mes objets business font appel à la dll pour recevoir leurs propriétés
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 void DataStruct1::assignBrol() { //blablabla m_brol = m_ptrToIface->getBrol1(int inParam) }
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 void DataStruct2::assignBrol() { //blablabla m_brol = m_ptrToIface->getBrol2(int inParam) }
J'utilise bien une interface abstraite pour interagir avec la dll
Donc rien d'inhabituel jusque là
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 #ifdef _CLASSINDLL #define _IFACE_CLASS_DECL __declspec(dllexport) #else #define _IFACE_CLASS_DECL __declspec(dllimport) #endif class _IFACE_CLASS_DECL IMyDllApi{ public: IMyDllApi(void); // blablabla int getBrol1(int inParam) = 0; int getBrol2(int inParam) = 0; }; extern "C" _IFACE_CLASS_DECL IMyDllApi* MyDllApi_New(void)
J'implémente...
le .h
le .cpp
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 //blablabla class MyDllApi public IMyDllApi{ public: MyDllApi(void); // blablabla int getBrol1(int inParam); int getBrol2(int inParam); };
Les objets de type Brol2 sont créés en fonction des résultats des Brol1 dans l'implémentation de Brol1.
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
18
19
20
21
22 //blablabla #include "Engine.h" //Engine est mon singleton qui orchestre un peu le processus extern "C" _IFACE_CLASS_DECL IMyDllApi* MyDllApi_New(void) { return new MyDllApi (); } int MyDllApi::getBrol1(int param) { int result; if Engine::getInstance()->getBrol1FromCache(param,result) return result; else return Engine::getInstance()->processBrol1(param) } int MyDllApi::getBrol2(int param) { int result; return Engine::getInstance()->getBrol2(param) }
Ma méthode processBrol1 est couteuse en calculs, donc je stocke les résultats déjà calculés dans une map membre de Engine. Les résultats pour les Brol2 sont aussi stockés dans Engine.
l'implémentation de Engine
Finalement mon brol1
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
18
19
20
21
22
23
24
25
26
27
28
29 #include "brol1.h" #include "brol2.h" // blabla // implémentation du singleton : initialisation du membre instance static, implémentation de getInstance() // blablabla int Engine::processBrol1(param) { newBrol = Brol1(param); int val = newBrol->getVal(); m_brols1Map[param] = val; return val; } int Engine::getBrol2(param) { map<int,int>::iterator it = m_brols2Map.find(param); if (it == m_brols2Map.end()) return -1; else return (*it).second; } void Engine::addBrol1(param, val) { m_brols1Map[param] = val; } void Engine::addBrol2(param, val) { m_brols2Map[param] = val; }
Désolé pour la tartine de code.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 //blablabla #include "brol2.h" Brol1::Brol1(int param) { //code couteux myBrol = Brol2(param); //l'initialisation du Brol2 est lourde également Engine::getInstance()->addBrol2(param, myBrol.getVal()); }
Donc le problème est le suivant : alors que je pensais que l'utilisation du singleton garantissait une seule instance de l'objet dans l'espace mémoire de la dll, je me retrouve avec des instances différentes de Engine dans MyDllApi::getBrol2(int param) et Brol1::Brol1(int param).
C'est comme si Engine était réinstancié à chaque fois que je fais appel à l'interface de ma dll alors que la dll n'est chargée qu'une seule fois en mémoire. De même, les résultats intermédiaires qui sont stockés ne savent pas être récupérés car l'instance est différente.
Bizarrement dans l'implémentation réelle, j'ai un ou deux singletons dans ma dll qui contiennent ses settings ainsi que les fonctions d'accès à la DB du client qui marchent sans aucun problème
Comme dit plus haut, j'ai pu trouver un workaround en conservant le pointeur vers l'instance et en le passant en paramètre à Brol1 ou bien avec un global mais je ne trouve pas ça fort joli ou alors ça alourdit mes méthodes et fonctions en donnant à chaque fois le pointeur vers l'instance alors que c'est justement ce que l'utilisation du singleton doit éviter.
Est-ce un problème de mauvaise compréhension de static, des paramètres à du compilo ou des instructions/macro préprocesseurs à donner à VS2005 ?
Merci
Partager