Bonjour,
J'ai un problème, qui dans sa version de base à déjà été écvoquer / partiellement résolu.
Ce problème de base est :
Comment partager l'instance d'un singleton entre une Dll et L'Exe si chaqun fait appel à getInstance dans son propre espace mémoire ?
voici un exemple de code source correspondant à ce problème :
Le template des singletons génériques :
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
| #ifndef SINGLETON
#define SINGLETON
template <class S>
class Singleton
{
private:
static S * instance;
//static S Instance;
public:
/*
static S & Get ()
{
static S Instance; // règle le cas de l'ordre de construction
// mais pas celui de l'ordre de destruction
return Instance;
}
*/
/**/
static S * Get ()
{
if(!instance)
instance = new S;
return instance;
}
/**/
};
//template <class S>
//S Singleton<S>::Instance;
template <class S>
S* Singleton<S>::instance = 0;
#endif |
le singleton qui en dérive et en instancie par la même occasion (si on peut parler d'instanciation de template) :
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
| #include <iostream>
using namespace std;
class TheSingleton
: Singleton <TheSingleton> // magie!
{
// sans ceci, le parent ne peut instancier son enfant
friend class Singleton <TheSingleton>;
TheSingleton (): m_Cur (0)
{
cout<<endl<<"Nouveau Singleton !!!"<<endl<<endl;
};
~TheSingleton(){};
int m_Cur;
public:
// vos services vont ici
int Prochain()
{
return ++m_Cur;
};
//static TheSingleton * Get() {return Singleton<TheSingleton>::Get();}
};
#endif |
la classe principale de ma DLL exemple :
.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #ifndef TESTSINGLETONDLL
#define TESTSINGLETONDLL
#include "TheSingleton.h"
#include "dllImportExport.h"
#include <iostream>
using namespace std;
class DECLSPECIFIER_DLL TestSingletonDll
{
public:
TestSingletonDll (){};
~TestSingletonDll(){};
int Next();
};
#endif |
.cpp
1 2 3 4 5 6 7
| #include "TestSingletonDll.h"
int TestSingletonDll::Next()
{
return Singleton<TheSingleton>::Get()->Prochain();
//return TheSingleton::Get()->Prochain();
} |
la classe principale de mon executable :
.h
1 2 3 4 5 6 7 8 9 10 11 12 13
| #ifndef TESTSINGLETONMAIN
#define TESTSINGLETONMAIN
#include "TestSingletonDll.h"
#include <iostream>
using namespace std;
class TestSingletonMain
{
public:
TestSingletonMain(){};
};
#endif |
.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include "TestSingletonMain.h"
int main ()
{
int ij;
TestSingletonMain* m = new TestSingletonMain();
TestSingletonDll* d = new TestSingletonDll();
cout << " courant : "<<d->Next()<<endl;
cout << " courant : "<<d->Next()<<endl;
cout<< " courant : "<<Singleton<TheSingleton>::Get()->Prochain()<<endl;
//cout<< " courant : "<<TheSingleton::Get()->Prochain()<<endl;
cin >> ij;
} |
la sortie affichée :
1 2 3 4 5 6 7 8 9
|
Nouveau Singleton !!!
courant : 1
courant : 2
Nouveau Singleton !!!
courant : 1 |
Le problème ne semble donc pas résolu.
Finalement en exportant l'instanciation du singleton ça marche effectivement :
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
|
using namespace std;
//class TheSingleton;
//EXPIMP_TEMPLATE_DLL template class DECLSPECIFIER_DLL Singleton<TheSingleton>;
class DECLSPECIFIER_DLL TheSingleton
: Singleton <TheSingleton> // magie!
{
// sans ceci, le parent ne peut instancier son enfant
friend class Singleton <TheSingleton>;
TheSingleton (): m_Cur (0)
{
cout<<endl<<"Nouveau Singleton !!!"<<endl<<endl;
};
~TheSingleton(){};
int m_Cur;
public:
// vos services vont ici
int Prochain()
{
return ++m_Cur;
};
//static TheSingleton * Get() {return Singleton<TheSingleton>::Get();}
};
#endif |
avec dllImportExport.h :
1 2 3 4 5 6 7 8 9 10 11 12
| #ifndef DLLIMPORTEXPORT_H
#define DLLIMPORTEXPORT_H
//macro to define if we export (from a DLL) or Import (from the Exe) without changing the header
#ifdef EXP_BLOCK // so we are in a DLL Block
# define DECLSPECIFIER_DLL __declspec(dllexport)
# define EXPIMP_TEMPLATE_DLL
#else // so we are in the Exe
# define DECLSPECIFIER_DLL __declspec(dllimport)
# define EXPIMP_TEMPLATE_DLL extern
#endif
#endif |
Le problème de base est donc résolu.
La où je viens ici, c'est que contrairement a ce que j'ai présenté ici,
le problème est que dans ma véritable application, j'utilise des templates même en temps que singleton
(attention a ne pas confondre au singleton générique de base qui est lui aussi un template).
j'utilise quelque chose du genre :
1 2 3 4 5 6 7
|
template <class A,class B>
class TheTemplatedSingleton
: Singleton <TheTemplatedSingleton<A,B>>
{
...
} |
Pour l'instant je suis arrivé a une solution "médiane"
Le problème que j'ai est que l'on ne peut pas vraiment exporter un template :
1 2 3 4 5 6
| template <class A, class B>
class DECLSPECIFIER_DLL TheTemplatedSingleton
: Singleton <TheTemplatedSingleton<A,B>> // magie!
{
...
} |
ça build bien la Dll, mais du point de vu de l'exe :
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include "TestSingletonMain.h"
int main ()
{
int ij;
TestSingletonMain* m = new TestSingletonMain();
TestSingletonDll* d = new TestSingletonDll();
cout << " courant : "<<d->Next<int,int>()<<endl;
cout << " courant : "<<d->Next<int,int>()<<endl;
//cout<< " courant : "<<Singleton<TheTemplatedSingleton<int,int>>::Get().Prochain()<<endl;
cout<< " courant : "<< TheTemplatedSingleton<int,int>::Get().Prochain() <<endl;
cin >> ij;
} |
par contre le build ne finit pas, et débouche sur une erreur à l'étape de link :
1 2
| 1>TestSingletonMain.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: static class TheTemplatedSingleton<int,int> & __cdecl TheTemplatedSingleton<int,int>::Get(void)" (__imp_?Get@?$TheTemplatedSingleton@HH@@SAAAV1@XZ) referenced in function _main
1>TestSingletonMain.obj : error LNK2019: unresolved external symbol "public: int __thiscall TestSingletonDll::Next<int,int>(void)" (??$Next@HH@TestSingletonDll@@QAEHXZ) referenced in function _main |
bien sur si j'enlève les export/import :
1 2 3 4 5 6
| template <class A, class B>
class /*DECLSPECIFIER_DLL*/ TheTemplatedSingleton
: Singleton <TheTemplatedSingleton<A,B>> // magie!
{
...
} |
ça compile bien mais je me retrouve au problème initiale : duplication des instances :
1 2 3 4 5 6 7 8
| Nouveau Singleton !!!
courant : 1
courant : 2
Nouveau Singleton !!!
courant : 1 |
finallement je suis obliger de simuler l'exportation du template pour passer l'étape de link.
Cela se fait en declarant et en exportant les différentes instance de template que l'on va utiliser :
TheTemplatedSingleton.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
#include "TheTemplatedSingleton.h"
template <class A, class B>
TheTemplatedSingleton<A,B>::TheTemplatedSingleton (): m_Cur (0)
{
cout<<endl<<"Nouveau Singleton<"<<typeid(A).name()<<","<<typeid(B).name()<< "> !!!"<<endl<<endl;
}
template <class A, class B>
int TheTemplatedSingleton<A,B>::Prochain()
{
return ++m_Cur;
}
EXPIMP_TEMPLATE_DLL template class DECLSPECIFIER_DLL TheTemplatedSingleton<int,int>;
//EXPIMP_TEMPLATE_DLL template class DECLSPECIFIER_DLL Singleton<TheTemplatedSingleton<int,int>>; |
et
TestSingletonDll.cpp
1 2 3 4 5 6 7 8 9 10 11
|
#include "TestSingletonDll.h"
template<class A, class B>
int TestSingletonDll::Next()
{
//return Singleton<TheTemplatedSingleton<A,B>>::Get().Prochain();
return TheTemplatedSingleton<A,B>::Get().Prochain();
}
EXPIMP_TEMPLATE_DLL template DECLSPECIFIER_DLL int TestSingletonDll::Next<int,int>(); |
En ce cas je peut builder l'executable car celui-ci n'utilise que l'instance du singleton template :
TheTemplatedSingleton<int,int>
que je viens d'exporter explicitement (cette instance de TheTemplatedSingleton pour les type <int,int>)
et on peut voir que cela fonctionne effectivement :
1 2 3 4 5 6
|
Nouveau Singleton<int,int> !!!
courant : 1
courant : 2
courant : 3 |
Donc content ?
en fait NON car comme on le vois si on veut exporter un template, qui est singleton afin de n'avoir
qu'une seul instance (par instance de template!) partager entre la (les) Dll et l'exécutable,
il faut exporter explicitement TOUTES les instances possible du template que l'on serai a même d'utiliser au niveau de l'exécutable ...
bref plutôt contraignant ...
Existe t'il une solution/une parade a cela ? je suis preneur de toute information en ce sens
Partager