IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Variable statique dupliquée suite au chargement d'une dll avec visual c++


Sujet :

C++

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    248
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : Canada

    Informations forums :
    Inscription : Septembre 2008
    Messages : 248
    Points : 119
    Points
    119
    Par défaut Variable statique dupliquée suite au chargement d'une dll avec visual c++
    Bonjour,

    J'ai un programme C++ qui charge dynamiquement des dll. Les dll sont des plugins pour mon application qui peuvent être développés séparément. Lorsque l'application principale est lancée, tous les plugins sont chargés à partir d'un répertoire connu (ex. /bin/plugins).

    Dans le programme principale, j'utilise une classe "factory" pour créer des objets. La "factory" est aussi un "singleton". Voici le code de cette classe:

    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
     
    #ifndef _SOLUTION_FACTORY_H
    #define _SOLUTION_FACTORY_H
     
    #include <string>
     
    using std::string;
     
    class mscSolution;
    class mscUniverse;
     
    class SolutionFactory
    {
    public:
     
    	static SolutionFactory* getInstance();
    	mscSolution* createSolution(mscUniverse* u);
    	void setActiveModel(string& model){this->activeModel=model;}
     
    protected:
    	SolutionFactory();
     
    	static SolutionFactory* _instance;
    	string activeModel;
    };
     
    #endif
    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
     
    #include "SolutionFactory.h"
     
    #include "CCSolution.h"
    #include "BCSolution.h"
     
    SolutionFactory* SolutionFactory::_instance = NULL;
     
    //_______________________________________________________________________________________
    SolutionFactory* SolutionFactory::getInstance()
    {
    	if(_instance == NULL)
    	{
    		std::cout << "SolutionFactory instance created" << std::endl;
    		_instance = new SolutionFactory();
    	}
    	else
    	{
    		std::cout << "instance exists, activeMode = " << _instance->activeModel << std::endl; 
    	}
     
    	return _instance;
    }
     
    //_______________________________________________________________________________________
    mscSolution* SolutionFactory::createSolution(mscUniverse* u)
    {
    	if(activeModel == CCSolution::name)
    		return new CCSolution(u);
    	else if(activeModel == BCSolution::name)
    		return new BCSolution(u);
     
    	std::cerr << "The solution model " << activeModel << " was not found" << std::endl;
    	return NULL;
    }
     
    //_______________________________________________________________________________________
    SolutionFactory::SolutionFactory()
    {
    	this->activeModel = "INVALID";
    }
    Le premier appel à SolutionFactory se fait à partir de l'application principale et les appels qui suivent se font dans un plugin une fois la dll correspondante est chargée. Les appels se font de la façon suivante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    SolutionFactory::getInstance()->createSolution(universe);
    Suite au premier appel, qui est fait à partir de l'application principale, la sortie est la suivante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    SolutionFactory instance created
    Par la suite, le plugin est chargé et deux appels sont effectués à SolutionFactoryde la même façon. La sortie cette fois est:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    SolutionFactory instance created
    The solution model INVALID was not found
    instance exists, activeMode = INVALID
    The solution model INVALID was not found
    instance exists, activeMode = INVALID
    Apparemment, la variable statique _instance n'est pas visible au plugin qui est chargé dynamiquement à partir d'une dll et une deuxième copie de _instance est crée lorsque le "singleton" est appelé à partir du plugin.

    PS: Je compile avec visual c++.
    J'ai essayé le même code avec gcc et tout semble fonctionner correctement !!

    Est ce c'est un problème dans visual studio ? y a t-il un moyen de le contourner ?

    Merci !

  2. #2
    Membre éclairé
    Inscrit en
    Décembre 2010
    Messages
    290
    Détails du profil
    Informations forums :
    Inscription : Décembre 2010
    Messages : 290
    Points : 719
    Points
    719
    Par défaut
    Si ta classe SolutionFactory est compilee statiquement dans chaque binaire, effectivement _instance existera deux fois en memoire.

    La raison pour laquelle le comportement est different avec gcc, il me semble que c'est du au fait que dans le cas de binaires "dynamiques", les adresses des methodes appelees sont resolues au chargement du module, et non a sa compilation/linkage. Mais je n'ai jamais vraiment approfondi le sujet, alors je peux me tromper.

    Pour qu'une DLL accede a la meme variable _instance que ton module exe principal (ou qu'une autre DLL), il faut qu'il accede au meme objet. Les solutions sont variees, mais la plus simple c'est encore d'exporter cette classe depuis l'exe principal, et l'importer dynamiquement depuis la DLL.

  3. #3
    Membre régulier
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    248
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : Canada

    Informations forums :
    Inscription : Septembre 2008
    Messages : 248
    Points : 119
    Points
    119
    Par défaut
    Citation Envoyé par phi1981 Voir le message
    Si ta classe SolutionFactory est compilee statiquement dans chaque binaire, effectivement _instance existera deux fois en memoire.

    La raison pour laquelle le comportement est different avec gcc, il me semble que c'est du au fait que dans le cas de binaires "dynamiques", les adresses des methodes appelees sont resolues au chargement du module, et non a sa compilation/linkage. Mais je n'ai jamais vraiment approfondi le sujet, alors je peux me tromper.

    Pour qu'une DLL accede a la meme variable _instance que ton module exe principal (ou qu'une autre DLL), il faut qu'il accede au meme objet. Les solutions sont variees, mais la plus simple c'est encore d'exporter cette classe depuis l'exe principal, et l'importer dynamiquement depuis la DLL.
    Merci phi1981 pour ton explication. Je fais un peu de lecture pour voir comment je peux faire le Export/Import proprement.

    A+

  4. #4
    Membre régulier
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    248
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : Canada

    Informations forums :
    Inscription : Septembre 2008
    Messages : 248
    Points : 119
    Points
    119
    Par défaut
    Ok ca fonctionne finalement !

    Il a fallu créer une librairie dynamique partagée ("shared library") par le exe et les plugins. Les classes faisant partie de cette libraire dynamique doivent être importées/exportées.

    Dans le code ci-dessous, la macro MODEL_EXPORTS est définie dans la librairie partagée et non-définie dans les plugins et le exe.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    #ifdef WIN32
    	#ifdef MODEL_EXPORTS
    		#define MODEL_IMPORT_EXPORT __declspec(dllexport)
    	#else
    		#define MODEL_IMPORT_EXPORT __declspec(dllimport)
    	#endif
    #else
    	#define MODEL_IMPORT_EXPORT
    #endif
    La macro MODEL_IMPORT_EXPORT doit être ajoutée devant toutes les classes qui seront utilisées par le exe ou les plugins.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    class MODEL_IMPORT_EXPORT SolutionFactory
    {
    Dans windows, si la libraire utilisée est statique, le problème persiste. Avec gcc les deux fonctionnent. Comme expliqué par phi1981:
    Citation Envoyé par phi1981 Voir le message
    La raison pour laquelle le comportement est different avec gcc, il me semble que c'est du au fait que dans le cas de binaires "dynamiques", les adresses des methodes appelees sont resolues au chargement du module, et non a sa compilation/linkage.
    gcc permet aux dll (qui sont les plugins ici) de résoudre/linker aux symboles du exe durant le chargement. Ce n'est pas le cas avec visual studio où la résolution/linkage est effectuée à la compilation pour éviter un chargement plus lent. En séparant le code partagé dans une libraire dynamique, on est sure que la résolution/linkage est effectuée au chargement.

  5. #5
    Membre éclairé
    Inscrit en
    Décembre 2010
    Messages
    290
    Détails du profil
    Informations forums :
    Inscription : Décembre 2010
    Messages : 290
    Points : 719
    Points
    719
    Par défaut
    Il faut aussi noter qu'il est possible d'exporter une classe depuis l'exe principal, et l'importer dans le plug-in, donc sans passer par une DLL commune.
    Lorsqu'un .exe exporte des symboles, le linker génère une librairie d'import (un .lib qui n'est en fait là que pour implémenter les symboles en question) que l'on peut linker à chaque plugin. Le plugin, à son chargement via LoadLibrary, récupèrera les symboles exportés par l'exe.
    Désolé si mon explication n'est pas claire, mais tout ça pour dire qu'il n'est pas absolument nécessaire de passer par une DLL commune.

  6. #6
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Décembre 2010
    Messages
    734
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 734
    Points : 1 475
    Points
    1 475
    Par défaut
    Je me demandais si le problème ne serait pas résolu en utilisant non pas une variable statique avec accès Singleton, mais la transmission de la référence de l'instance au plugin pour l'initialiser. Dans ce cas ce serait l'exe qui contrôlerait l'instanciation de la factory, et tu pourrais t'assurer que ce n'est fait qu'une fois.

  7. #7
    Membre régulier
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    248
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : Canada

    Informations forums :
    Inscription : Septembre 2008
    Messages : 248
    Points : 119
    Points
    119
    Par défaut
    Citation Envoyé par therwald Voir le message
    Je me demandais si le problème ne serait pas résolu en utilisant non pas une variable statique avec accès Singleton, mais la transmission de la référence de l'instance au plugin pour l'initialiser. Dans ce cas ce serait l'exe qui contrôlerait l'instanciation de la factory, et tu pourrais t'assurer que ce n'est fait qu'une fois.
    Oui c'est vrai et j'y avais pensé mais contrairement à la solution précédente, cette solution nécessite une modification du code dans tous les plugins.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. compiler une dll avec visual studio 2005
    Par DebutantVisualStudio dans le forum Visual C++
    Réponses: 6
    Dernier message: 29/07/2009, 13h33
  2. lier une DLL avec visual c++ 2008
    Par Asmod_D dans le forum Visual C++
    Réponses: 2
    Dernier message: 19/07/2008, 14h10
  3. Contexte de chargement d'une DLL
    Par Guillemot dans le forum Windows
    Réponses: 2
    Dernier message: 10/07/2005, 09h34
  4. Chargement d'une DLL et utilisation du multithread
    Par Maitre Kanter dans le forum Langage
    Réponses: 6
    Dernier message: 07/09/2004, 23h18

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo