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

Langage C++ Discussion :

Problème d'instanciation multiple de static dans dll


Sujet :

Langage C++

  1. #1
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    3
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Septembre 2009
    Messages : 3
    Points : 1
    Points
    1
    Par défaut 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 : 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
    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)
    Donc rien d'inhabituel jusque là
    J'implémente...

    le .h
    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);
    };
    le .cpp
    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)
    }
    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 : 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;
    }
    Finalement mon brol1
    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());
    }
    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

  2. #2
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Bonjour et bienvenu,
    Il serait intéressant que tu nous montres ton implémentation du singleton (la static, le getinstance et tutti quanti).

  3. #3
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Il faudrait aussi poster ton implémentation de la fonction DllMain : il est possible que tu réinstancies tout à chaque attachement de processus / thread.

    Mais habituellement, pour garantir que l'on a bien un singleton au niveau système (et non pas au niveau processus uniquement), il faut passer par de la mémoire partagée...
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  4. #4
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 074
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 074
    Points : 12 120
    Points
    12 120
    Par défaut
    Les méthodes "getInstance" devraient être protégées contre le multi-thread.

    Le code ne semble ni complet ni standardisé.

  5. #5
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    3
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Septembre 2009
    Messages : 3
    Points : 1
    Points
    1
    Par défaut
    Le contenu de l'implémentation du singleton n'avancera pas beaucoup: en plus des méthodes déjà décrites pour son fonctionnement, c'est du classique

    on a donc en plus de ce que j'avais déjà dit dans le .cpp de Engine
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    Engine* Engine::m_instance = NULL;
    Engine* Engine::getInstance()
    {
    	if (!m_instance)
    	{
    		m_instance = new Engine();
    	}
    	return m_instance;
    }
    et dans la déclaration de la 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
     
    class Engine
    {
    public:
    	static Engine* getInstance();
    	virtual ~Engine(void);
    	int processBrol1(int param);
    	int getBrol2(int param);
    	void addBrol1(int param,int val);
    	void addBrol2(int param,int val)
    private:
    	Engine(void);
    	static Engine* m_instance;
    	map<int,int> brols1Map;
    	map<int,int> brols1Map;
    };
    Il faudrait aussi poster ton implémentation de la fonction DllMain : il est possible que tu réinstancies tout à chaque attachement de processus / thread.
    un singleton au niveau système (et non pas au niveau processus uniquement), il faut passer par de la mémoire partagée...
    Je n'utilise pas de DllMain et je n'attache qu'une seule fois la dll. Elle est chargée via un loadlibrary et l'invocation de MyDllApi_New au début de l'app principale. Une seule instance du pointeur vers l'interface est utilisée tout le long de l'appli.

    Les méthodes "getInstance" devraient être protégées contre le multi-thread.
    Je craignais aussi un problème de thread safety mais je ne fais mes appels que depuis un seul même thread (et je me fais déjà des cheveux blancs en prévision du portage multi-thread des algo de l'appli v)

  6. #6
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 074
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 074
    Points : 12 120
    Points
    12 120
    Par défaut
    Question un peu bébête, pourquoi ne pas mettre un point d'arrêt dans le constructeur du singleton. Vous aurez le motif de la multi-instanciation.

    Toute implémentation standard (correcte) d'un singleton est thread-safe, donc votre implémentation "classique" n'est pas si standard.

    Que cela vous amuse de ré-implémenter ces standards, soit, mais faite le correctement.

  7. #7
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par fredoule2k Voir le message
    Je n'utilise pas de DllMain et je n'attache qu'une seule fois la dll.
    Grosse erreur, ça : c'est la première chose à faire lorsque l'on a un souci de ce genre... Implémente un DllMain, et pose des points d'arrêt dessus (en plus des BP sur le constructeur du singleton bien entendu). Tu risques de voir des choses inattendues.
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  8. #8
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    3
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Septembre 2009
    Messages : 3
    Points : 1
    Points
    1
    Par défaut
    Merci pour le tuyau du dllmain: avec la structure de mon app, je ne voyais pas vraiment l'utilité du bazar en regardant "dll" dans msdn.

    L'hisoire de singleton thread-safe (même si je ne pense pas que ça me concerne) m'a permis de googler et touver plein de problématique sur ce sujet pour quand je m'intéresserai au multi-thread (alors que je pensais que les problèmes de synchronisation, deadlock, famine étaient déjà très casse-gueule )

    Je regarderai donc à ces bons conseils

Discussions similaires

  1. Instanciations multiples de contrôles dans Silverlight
    Par mlebreton dans le forum Silverlight
    Réponses: 2
    Dernier message: 20/01/2009, 22h43
  2. Problème d'instanciation multiples
    Par teddyalbina dans le forum C++/CLI
    Réponses: 5
    Dernier message: 04/07/2008, 18h14
  3. Réponses: 0
    Dernier message: 05/03/2008, 12h20
  4. Réponses: 3
    Dernier message: 29/04/2006, 11h58
  5. Problème d'ajout multiples dans un BDD Access
    Par arnaud_verlaine dans le forum Langage SQL
    Réponses: 3
    Dernier message: 31/05/2004, 13h34

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