Singleton dans une DLL et espace mémoire propre

Singleton dans une DLL et espace mémoire propre - C++ - Programmation

Marsh Posté le 17-11-2005 à 17:50:00    

Bonjour à tous,
 
J'ai une classe singleton Montage_Gestion qui se trouve dans une DLL nommé Montage.
 
J'ai remarqué le problème suivant:
 
quand j'instancie Montage_Gestion à l'intérieur de la DLL, j'ai bien une seule et unique instance de ce singleton, pas de soucis.
 
Si par la suite je passe par l'application .EXE pour instancier ce singleton, il ne me retourne pas le pointeur de Montage_Gestion déjà existant, mais il  
m'en crée une nouvelle instance.
 
J'ai cru comprendre que c'est parce que la DLL et l'application utilisent des segments de mémoire différents et que le singleton utilise une variable statique.
 
Seulement je ne sais pas comment y remédier.
 
Quelqu'un saurait comment faire?
 
Merci d'avance
 
Mike

Reply

Marsh Posté le 17-11-2005 à 17:50:00   

Reply

Marsh Posté le 17-11-2005 à 18:01:30    

Je ne crois pas que tu puisses partager des donnees entre deux programmes via une DLL. Regarde plutot du cote de COM.


---------------
The Million Dollar Screenshot  (seo v7ndotcom elursrebmem paesys wifi)
Reply

Marsh Posté le 17-11-2005 à 18:06:27    

déclare le pointeur du singleton dans un segment de mémoire partagée en utilisant le pragma data_seg :

Code :
  1. #pragma data_seg (".shared" )
  2. Singleton *ptrSingleton = new Singleton();
  3. #pragma data_seg()


 
un peu de lecture :
http://msdn.microsoft.com/library/ [...] lls.3f.asp


Message édité par Harkonnen le 17-11-2005 à 18:14:37
Reply

Marsh Posté le 18-11-2005 à 01:45:13    

Finalement plus simple, j'utilisais une version template du singleton.
 
Il a suffi que j'en fasse un singleton "normal" pour que tout fonctionne nickel.
 
Merci!

Reply

Marsh Posté le 18-11-2005 à 14:02:42    

si je comprends bien, avec la solution d'Harko, si plusieurs exécutable utilisent le singleton, seul le pointeur sera en mémoire partagée. donc le pointeur pointera vers un espace mémoire accessible que pour un seul processus ?


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 19-08-2006 à 23:31:13    

Si j'ai bien compris (ce dont je ne suis pas sûr), Harkonnen ne résoud pas le bon problème : il résoud le problème d'une DLL partagée entre plusieurs processus, chaque processus ayant une copie des variables statiques, et donc un singleton chacun. Le problème d'haazheel serait plutot lié au fait que, j'ai cru comprendre, il a une classe singleton template, dont il fait hériter tous ses singletons. Il a donc son pointeur d'instance statique déclaré de façon template, et donc il n'est pas compilé à la création de la DLL qui contient cette classe template (ça n'a d'ailleurs pas de sens), mais dans chaque DLL (ou exe) qui inclue cet header. Donc quand une fonction compilée pour une DLL accède au pointeur, elle accède au pointeur de cette DLL, et il peut donc il y avoir plusieurs pointeurs (ce qui est embêtant dans le cas d'un singleton ^^).  
 
La solution que je proposerais serait plutot de rajouter, dans chaque classe singleton qui hérite du template singleton, une fonction "getInstance" (ou un autre nom, mais le même que celui de la classe template en tout cas) qui surcharge la fonction de la classe template, et qui appelle la fonction de la dite classe template. Je suis conscient que ce que je dis n'est pas clair, donc un petit exemple (sans rentré dans les détails, ça ne compile probablement pas ^^) :
 
le fichier qui contient la classe singleton template :

Code :
  1. template <class T>
  2. class Singleton
  3. {
  4.    static T* instance;
  5.    T* getInstance()
  6.    {
  7.       if(instance) return instance;
  8.       instance = new T;
  9.       return instance;
  10.    }
  11. }
  12. template <class T> Singleton<T>::instance = 0; //la variable statique, qui sera (si j'ai bien compris) dupliquée dans toutes
  13.                                                                  //les DLL qui compileront Singleton<T>. A noter que cette variable représente
  14.                                                                  //de toute façon plusieurs variables, puisque c'est un template...


 
un fichier qui définie une classe singleton héritant de notre template :

Code :
  1. class UnSingleton : public Singleton<UnSingleton>
  2. {
  3.     //la fonction magique qui permet, quand on veut récupérer l'instance de UnSingleton, d'être sûr de
  4.     //passer toujours par la même DLL, puisque cette fois ci nous ne sommes pas dans une classe template
  5.     UnSingleton *getInstance() {return Singleton<UnSingleton>::getInstance();}
  6. }


 
voilà, normalement en faisant comme ça, ça fonctionne, à condition de ne jamais utiliser directement Singleton<T>::getInstance puisque si l'on fais ça, on utilise la variable statique "instance" de la DLL dans laquelle on fais cet appel, et non pas celui de la DLL dans laquelle on a compilé la classe singleton T (la classe UnSingleton dans l'exemple).


Message édité par yawen le 19-08-2006 à 23:33:06
Reply

Marsh Posté le 06-08-2008 à 15:05:31    

Bonjour.
Désoler de déterrer un vieux topic, mais la situation présentée ici se rapproche beaucoup de mon problème.
Tout d'abord replaçons nous dans le cas de ce topic, car la réponse donné au dernier post ne semble pas résoudre le problème.
Je présente d'abord les codes sources :
Le template des singletons génériques :

Code :
  1. #ifndef SINGLETON
  2. #define SINGLETON
  3. template <class S>
  4. class Singleton
  5. {
  6. private:
  7. static S * instance;
  8. //static S Instance;
  9. public:
  10. /*
  11.   static S & Get ()
  12.   {
  13.  static S Instance; // règle le cas de l'ordre de construction
  14.      // mais pas celui de l'ordre de destruction
  15.  return Instance;
  16.   }
  17.   */
  18.   /**/
  19. static S * Get ()
  20. {
  21.  if(!instance)
  22.   instance = new S;
  23.  return instance;
  24. }
  25. /**/
  26. };
  27. //template <class S>
  28. //S Singleton<S>::Instance;
  29. template <class S>
  30. S* Singleton<S>::instance = 0;
  31. #endif


 
le singleton qui en dérive et en instancie par la même occasion (si on peut parler d'instanciation de template) :

Code :
  1. #include <iostream>
  2. using namespace std;
  3. class TheSingleton
  4.    : Singleton <TheSingleton> // magie!
  5. {
  6.    // sans ceci, le parent ne peut instancier son enfant
  7. friend class Singleton <TheSingleton>;
  8. TheSingleton (): m_Cur (0)
  9. {
  10.  cout<<endl<<"Nouveau Singleton !!!"<<endl<<endl;
  11. };
  12. ~TheSingleton(){};
  13. int m_Cur;
  14. public:
  15. // vos services vont ici
  16. int Prochain()
  17. {
  18.  return ++m_Cur;
  19. };
  20. static TheSingleton * Get() {return Singleton<TheSingleton>::Get();}
  21. };
  22. #endif


 
la classe principale de ma DLL exemple :
.h

Code :
  1. #ifndef TESTSINGLETONDLL
  2. #define TESTSINGLETONDLL
  3. #include "TheSingleton.h"
  4. #include "dllImportExport.h"
  5. #include <iostream>
  6. using namespace std;
  7. class DECLSPECIFIER_DLL TestSingletonDll
  8. {
  9. public:
  10. TestSingletonDll (){};
  11. ~TestSingletonDll(){};
  12. int Next();
  13. };
  14. #endif


.cpp

Code :
  1. #include "TestSingletonDll.h"
  2. int TestSingletonDll::Next()
  3. {
  4. //return Singleton<TheSingleton>::Get().Prochain();
  5. return TheSingleton::Get()->Prochain();
  6. }


 
la classe principale de mon executable :
.h

Code :
  1. #ifndef TESTSINGLETONMAIN
  2. #define TESTSINGLETONMAIN
  3. #include "TestSingletonDll.h"
  4. #include <iostream>
  5. using namespace std;
  6. class TestSingletonMain 
  7. public:
  8. TestSingletonMain(){};
  9. };
  10. #endif


.cpp

Code :
  1. #include "TestSingletonMain.h"
  2. int main ()
  3. {
  4. int ij;
  5. TestSingletonMain* m = new TestSingletonMain();
  6. TestSingletonDll* d = new TestSingletonDll();
  7. cout << " courant : "<<d->Next()<<endl;
  8. cout << " courant : "<<d->Next()<<endl;
  9. //cout<< " courant : "<<Singleton<TheSingleton>::Get().Prochain()<<endl;
  10. cout<< " courant : "<<TheSingleton::Get()->Prochain()<<endl;
  11. cin >> ij;
  12. }


 
la sortie affichée :  

Code :
  1. Nouveau Singleton !!!
  2. courant : 1
  3. courant : 2
  4. Nouveau Singleton !!!
  5. courant : 1


 
Le probleme ne semble donc pas résolu.
 
haazheel, initiateur de ce topic disait :

haazheel a écrit :

Finalement plus simple, j'utilisais une version template du singleton.
 
Il a suffi que j'en fasse un singleton "normal" pour que tout fonctionne nickel.
 
Merci!


 
Le problème c'est que dans ma véritable application (les sources précentes n'était que des exemple pour des tests), j'utilise des template 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'utilisa quelque chose du genre :

Code :
  1. template <class A,class B>
  2. class TheTemplatedSingleton
  3.    : Singleton <TheTemplatedSingleton<A,B>>
  4. {
  5.     ...
  6. }

Message cité 1 fois
Message édité par el_trex le 06-08-2008 à 15:06:25
Reply

Marsh Posté le 06-08-2008 à 16:02:13    

Finalement pour l'exemple de base de ce topic, en exportant l'instanciation du singleton ça marche effectivement :
 

Code :
  1. using namespace std;
  2. //class TheSingleton;
  3. //EXPIMP_TEMPLATE_DLL template class DECLSPECIFIER_DLL Singleton<TheSingleton>;
  4. class DECLSPECIFIER_DLL TheSingleton
  5.    : Singleton <TheSingleton> // magie!
  6. {
  7.    // sans ceci, le parent ne peut instancier son enfant
  8. friend class Singleton <TheSingleton>;
  9. TheSingleton (): m_Cur (0)
  10. {
  11.  cout<<endl<<"Nouveau Singleton !!!"<<endl<<endl;
  12. };
  13. ~TheSingleton(){};
  14. int m_Cur;
  15. public:
  16. // vos services vont ici
  17. int Prochain()
  18. {
  19.  return ++m_Cur;
  20. };
  21. //static TheSingleton * Get() {return Singleton<TheSingleton>::Get();}
  22. };
  23. #endif


 
Il n'y a pas besoin de redéfinir la fonction d'accès sur l'instance du singleton (

Code :
  1. static TheSingleton * Get()

) d'ailleur pour que cela marche.
 
pour ce qui est du

Code :
  1. //class TheSingleton;
  2. //EXPIMP_TEMPLATE_DLL template class DECLSPECIFIER_DLL Singleton<TheSingleton>;


je ne suis très au clair de ce que ça apporte de déclarer le template comme externe ... peut être justement dans mon cas :

el_trex a écrit :


 
...
 
Le problème c'est que dans ma véritable application (les sources précentes n'était que des exemple pour des tests), j'utilise des template 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'utilisa quelque chose du genre :

Code :
  1. template <class A,class B>
  2. class TheTemplatedSingleton
  3.    : Singleton <TheTemplatedSingleton<A,B>>
  4. {
  5.     ...
  6. }



 
cela me sera utile ...  
 
Je poursuit mes test en dans ce cas de figure a présent que le problème de base est résolu.


Message édité par el_trex le 06-08-2008 à 16:08:41
Reply

Marsh Posté le 06-08-2008 à 18:20:12    

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 :

Code :
  1. template <class A, class B>
  2. class DECLSPECIFIER_DLL TheTemplatedSingleton
  3.    : Singleton <TheTemplatedSingleton<A,B>> // magie!
  4. {
  5. ...
  6. }


ça build bien la Dll, mais du point de vu de l'exe :

Code :
  1. #include "TestSingletonMain.h"
  2. int main ()
  3. {
  4. int ij;
  5. TestSingletonMain* m = new TestSingletonMain();
  6. TestSingletonDll* d = new TestSingletonDll();
  7. cout << " courant : "<<d->Next<int,int>()<<endl;
  8. cout << " courant : "<<d->Next<int,int>()<<endl;
  9. //cout<< " courant : "<<Singleton<TheTemplatedSingleton<int,int>>::Get().Prochain()<<endl;
  10. cout<< " courant : "<< TheTemplatedSingleton<int,int>::Get().Prochain() <<endl;
  11. cin >> ij;
  12. }


 par contre le build ne finit pas, et débouche sur une erreur à l'étape de link :

Code :
  1. 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
  2. 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 :

Code :
  1. template <class A, class B>
  2. class /*DECLSPECIFIER_DLL*/ TheTemplatedSingleton
  3.    : Singleton <TheTemplatedSingleton<A,B>> // magie!
  4. {
  5. ...
  6. }


ça compile bien mais je me retrouve au problème initiale : duplication des instances :

Code :
  1. Nouveau Singleton !!!
  2. courant : 1
  3. courant : 2
  4. Nouveau Singleton !!!
  5. 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:

Code :
  1. #include "TheTemplatedSingleton.h"
  2. template <class A, class B>
  3. TheTemplatedSingleton<A,B>::TheTemplatedSingleton (): m_Cur (0)
  4. {
  5.  cout<<endl<<"Nouveau Singleton<"<<typeid(A).name()<<","<<typeid(B).name()<< "> !!!"<<endl<<endl;
  6. }
  7. template <class A, class B>
  8. int TheTemplatedSingleton<A,B>::Prochain()
  9. {
  10.  return ++m_Cur;
  11. }
  12. EXPIMP_TEMPLATE_DLL template class DECLSPECIFIER_DLL TheTemplatedSingleton<int,int>;
  13. //EXPIMP_TEMPLATE_DLL template class DECLSPECIFIER_DLL Singleton<TheTemplatedSingleton<int,int>>;


 
et
 
TestSingletonDll.cpp

Code :
  1. #include "TestSingletonDll.h"
  2. template<class A, class B>
  3. int TestSingletonDll::Next()
  4. {
  5. //return Singleton<TheTemplatedSingleton<A,B>>::Get().Prochain();
  6. return TheTemplatedSingleton<A,B>::Get().Prochain();
  7. }
  8. 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 :

Code :
  1. 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 :

Code :
  1. Nouveau Singleton<int,int> !!!
  2. courant : 1
  3. courant : 2
  4. 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 ;)


Message édité par el_trex le 07-08-2008 à 10:46:54
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed