Problème d'instanciation multiple de static dans dll
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:
1 2 3 4 5 6
|
void DataStruct1::assignBrol()
{
//blablabla
m_brol = m_ptrToIface->getBrol1(int inParam)
} |
Code:
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
Code:
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) |
Donc rien d'inhabituel jusque là
J'implémente...
le .h
Code:
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);
}; |
le .cpp
Code:
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)
} |
Les objets de type Brol2 sont créés en fonction des résultats des Brol1 dans l'implémentation de Brol1.
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
Code:
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;
} |
Finalement mon brol1
Code:
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());
} |
Désolé pour la tartine de code.
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