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

Visual C++ Discussion :

La portée du static au sein de differents projets


Sujet :

Visual C++

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Février 2007
    Messages
    73
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 73
    Points : 69
    Points
    69
    Par défaut La portée du static au sein de differents projets
    Bonjour à tous,

    Voila j'ai (venant de Java/C#) voulu faire un objet qui soit accessible partout dans mon code.
    Rien de plus logique pour moi donc de faire un header avec l'objet se terminant par:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    static MonObjet MonInstanceObjet;
    Si bien que partout dans le code je pourrais faire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    #include "monobjet.h"
     
    MonInstanceObjet.WriteCoucou();
    Seulement j'ai découvert que le static en C++ provoque de très nombreuse instanciation. Je m'en suis rendu compte en affichant un petit texte de test avec le "cout" dans le constructeur.

    J'ai donc tout logiquement cherché sur le net et de lien en lien j'en suis venu à trouver ce site: http://www.parashift.com/c++-faq-lit...html#faq-10.12 qui parle de "static initialization order fiasco".

    J'ai donc mis mon static dans une fonction static:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     MonObjet& GetInstanceObjet()
     {
       static MonObjet* obj = new MonObjet();
       return *obj;
     }
    #define MonInstanceObjet GetInstanceObjet()
    et cette fois c'est mieux...
    Mais pas parfait, je suis encore étonné de trouver 3 instance faite (au lieu d'une vingtaine précédemment correspondant au nombre de fois où je l'utilise). Malheureusement ce n'est pas ce que je souhaite puisque je n'en veux qu'une !

    Après de méticuleuse recherche, j'ai trouvé ce qui pourrait être la solution:
    J'ai 3 projet qui l'utilise dans la solution. Mon premier projet dépend d'un second projet qui dépend lui-même d'un troisième (et l'objet se trouve dans le troisième).
    J'en conclut donc que Visual compile projet par projet et offre une portée du static au projet uniquement.

    D'où la question: comment faire pour que le static dans la méthode globable se propage au sein du code de la solution entière?

    Lucyberad.

    [EDIT: j'utilise VS2008 en projet non-managé]

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 157
    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 157
    Points : 12 271
    Points
    12 271
    Par défaut
    Vous êtes très loin de la solution.
    Il ne faut jamais instancier d'objet dans les .h.
    Il n'y pas de constructeur static en C++.
    Les globales, comme dans tous les langages, c'est mal.
    Vous essayez d'implémenter un Patern Singleton complètement unsafe.

    Je ne cautionne donc pas le code suivant : (et pas testé)

    Dans le .h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    class MonObjet
    {
    private :
    static MonObjet* s_Instance = null;
     
    public :
    static MonObjet& GetInstanceObjet();
    }
    Dans le cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
     
    MonObjet& MonObjet::GetInstanceObjet()
    {
        if(s_Instance == null)
        {
            s_Instance = new MonObjet();
        }
        return *s_Instance ;
    }

  3. #3
    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,
    Il y a à la base une confusion sur static.
    En C++, une variable globale statique n'est pas partagée par tous les objets d'un projet mais reste confinée dans l'unité de compilation (le .obj) où elle est déclarée. Donc mettre un static dans un .h et en inclure le .h dans différents .cpp revient à créer autant d'instances de la variable.
    Le singleton te permet d'avoir effectivement une seule variable pour toute l'application :
    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
     
    class singleton
    {
    private :
    static singleton* m_instance;
     
    public :
    /**
    @pre m_instance!=0
    */
    static singleton& instance();
     
    /**
     @pre m_instance==0
     @post m_instance!=0
    */
    static void create();
     
    /**
     @pre m_instance!=0
     @post m_instance==0
    */
    static void release();
     
    private:
       // avec un constructeur privé, seul la classe singleton peut créer un objet.
       singleton();
     
       // ces deux méthodes sont déclarées mais non définies pour interdire
       // la copie de l'objet (cf <a href="http://cpp.developpez.com/faq/cpp/?page=copiables" target="_blank">F.A.Q.</a>)
       singleton(singleton const&);
       singleton&operator=(singleton const&);
    };
    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
    singleton* singleton::m_instance=0;
    singleton& singleton::instance()
    {
       return *m_instance;
    }
     
    void singleton::create()
    {
       m_instance = new singleton;
    }
     
    void singleton::release()
    {
       delete m_instance;
       m_instance = 0;
    }
    Pour éviter la duplication dans les différents projets de ta solution, un seul projet doit compiler la définition (le .cpp), les autres ne doivent avoir que la déclaration (le .h).

  4. #4
    Membre du Club
    Profil pro
    Inscrit en
    Février 2007
    Messages
    73
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 73
    Points : 69
    Points
    69
    Par défaut
    "Les globales, comme dans tous les langages, c'est mal."
    Oui mais pourtant c'est très utile et quand c'est bien amené c'est pas une si grande erreur que ca (a mon gout)...
    Je pense à l'objet "Console" dans C#, "System.out" dans Java et "cout" dans C++... Ce sont des globales.
    Après avoir son modèle de donnée en globale, je pense comme toi, c'est un blasphème. Mais quand c'est de l'utilitaire comme l'accès à la sortie console, c'est tout a fait viable. Sinon j'attend qu'à m'instruire et qu'on m'explique ce qu'il y a de mal dans ces pratique que les principaux langages utilisent dans leurs implémentations.

    "Pour éviter la duplication dans les différents projets de ta solution, un seul projet doit compiler la définition (le .cpp), les autres ne doivent avoir que la déclaration (le .h)."
    Ce qui revient à tout mettre dans le même projet sous Visual ce qui surcharge rapidement... ou modifier le projet qui compile pour s'étendre aux autres en désactivant le compilo des autres ce qui n'est pas franchement intuitif et propre... je vais revoir mon modèle sur ce point, c'est mieux je pense et je peux me le permettre (à première vue).

    Merci des infos.

  5. #5
    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
    Citation Envoyé par Lucyberad Voir le message
    Citation Envoyé par 3DArchi
    Pour éviter la duplication dans les différents projets de ta solution, un seul projet doit compiler la définition (le .cpp), les autres ne doivent avoir que la déclaration (le .h).
    Ce qui revient à tout mettre dans le même projet sous Visual ce qui surcharge rapidement... ou modifier le projet qui compile pour s'étendre aux autres en désactivant le compilo des autres ce qui n'est pas franchement intuitif et propre... je vais revoir mon modèle sur ce point, c'est mieux je pense et je peux me le permettre (à première vue).

    Merci des infos.
    En toute logique ta solution comporte un exécutable et plusieurs bibliothèques (DLL ou statiques). Une de ces bibliothèques 'fournit' le service défini par ton singleton. C'est cette bibliothèque qui doit compiler le .cpp. Les autres utilisateurs de cette bibliothèque, l'exécutable et/ou d'autres bibliothèques incluent le fichier .h et se lient avec le .lib pour bénéficier du code.
    J'avoue que je comprend mal ton exposé du problème et ta réponse. Peux-tu préciser brièvement ton contexte (les types de projets liés entre eux) et ce qui te chagrine ?

  6. #6
    Membre du Club
    Profil pro
    Inscrit en
    Février 2007
    Messages
    73
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 73
    Points : 69
    Points
    69
    Par défaut
    J'ai 3 projets. Il en est bien sur 1 qui est l'exécutable.
    Les deux autres se dépendent mutuellement et pour se faire lien, ont un include l'un de l'autre. Le projet de l'exécutable les inclut tout les deux.
    C'est à dire que le premier projet fait un include des 2 .lib des deux autres projets. De plus, pour que les liens puissent se faire sur les .h, ce projet "executable" possède aussi comme propriété "Autres repertoires includes" 2 chemin vers la racine des deux autres projets.
    Les deux autres projets sont d'ailleurs en statique (pas de DLL donc).
    Mais comme ce sont 3 projet, le compilo de Visual passe 3 fois pour compiler séparément et assemble ensuite le tout. Je me demandait alors (en posant la question sur ce forum) s'il existait une option qui permet de dire aux deux autres projets d'être compilé AVEC le premier projet ou que le premier projet compile les deux autres avec lui. J'ai cherché avant, dans les options de visual et sur le net mais sans succès.

    Mon objet partagé est un objet qui permet la gestion d'un log et fonctionne à la manière d'un "cout". Je voulais mettre dans un tableau (vector) les differents log (qui sont composé de 3 données: "temps [timestamp] + severité [enum] + message [string]") pour pouvoir le filer en brut par mail lorsque une exception au sein du programme est levée et attrapée à l'aide d'un formulaire de rapport de bug. Mais je me suis vite rendu compte que mon tableau était totalement incomplet. De ce fonctionnement indésirable, j'ai traqué les différentes instances pour en comprendre que c'était une vingtaine d'instance qui étaient crées (et non pas une seule). J'ai donc découvert le "Static Fiasco" présenté sur le site que j'ai donné en lien dans mon précédent post, mais malgré la résolution donnée (qui est un singleton en fait) j'ai toujours 3 instance séparée... J'ai vite compris que c'était un problème de compilation par projet puisque un rajout d'un 4ème projet qui utilise cet objet rajoute 1 instance.

    Au final, je peux outrepasser cette contrainte comme un avantage, c'est à dire vraiment faire que 3 logs soit fait (avec 1 instance par projet), puis ensuite les envoyer tout les 3 par mail (en accédant à partir du premier projet, où le formulaire de soumission de bug se trouve, aux instances des deux autres par des fonctions se trouvant dans les deux autres qui retourne l'objet de log de leur projet).
    C'est fonctionnel, ca permet de séparer les trois, d'un coté ca ajoute en clarté par la séparation des 3 logs mais retire compréhension de la trame du temps (puisqu'il faut remettre dans l'ordre en fonction du temps attribué lors de l'ajout du log) donc je n'y perd pas vraiment ni ne gagne...

    Après, ca peux passer en séparé pour ce système, mais je pense que certains (et probablement moi plus tard) seront face au même problème sans y trouver de solution... donc s'il y en a une, c'est toujours bon à savoir...

  7. #7
    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
    Salut,
    En préambule, avoir 2 bibliothèques qui dépendent mutuellement l'une de l'autre est un indicateur d'un possible problème de conception.
    Ensuite, sur le fait d'avoir le code compilé 3 fois. Ici aussi, il faut bien s'entendre de quoi on parle. La dépendance de l'exécutable vis à vis des autres bibliothèques peut être indiquée à Visual dans les options du projet. Ainsi, avant de générer l'exécutable, les bibliothèques sont compilées si besoin.
    Quand au fait que le source est compilé 3 fois. Là ce n'est pas tout à fait exact. Un .cpp ne devrait être attaché qu'à un seul projet. Il sera compilé dans le cadre de ce projet. Ensuite, tu vas me dire que le .h est compilé lui 3 fois. En fait, oui et non. Le .h est compilé à chaque fois qu'un .cpp fait un include de cet en-tête. Et ce, que le .cpp implémentant l'en-tête soit dans le même projet ou non.
    Une possibilité pour accélérer ces compilations est d'utiliser des en-têtes pré-compilés. Mais en général, on met dans ces fichiers que des en-têtes stables et ayant vraiment une portée 'globale' sur le projet. Une classe de log pourrait effectivement rentrer dans ce cas.
    Sur la classe de log, c'est typiquement le genre de chose qui pourrait faire partie d'une bibliothèque à part entière, qui contiendrait l'instance de la variable globale 'cout' et offrirait via un en-tête adéquat de quoi y accéder et faire les opérations de trace.

  8. #8
    Membre du Club
    Profil pro
    Inscrit en
    Février 2007
    Messages
    73
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 73
    Points : 69
    Points
    69
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    En préambule, avoir 2 bibliothèques qui dépendent mutuellement l'une de l'autre est un indicateur d'un possible problème de conception.
    L'exemple, tu le donne plus bas: 1 classe executable et sa librairie opératoire + 1 ajoutée par les deux qui est la classe Log.
    Je vois pas où est l'erreur...

    Citation Envoyé par 3DArchi Voir le message
    Ensuite, sur le fait d'avoir le code compilé 3 fois. Ici aussi, il faut bien s'entendre de quoi on parle. La dépendance de l'exécutable vis à vis des autres bibliothèques peut être indiquée à Visual dans les options du projet. Ainsi, avant de générer l'exécutable, les bibliothèques sont compilées si besoin.
    N'est-ce pas ce qui est fait quand visual produit un .lib pour une bibliothèque?

    Citation Envoyé par 3DArchi Voir le message
    Quand au fait que le source est compilé 3 fois. Là ce n'est pas tout à fait exact. Un .cpp ne devrait être attaché qu'à un seul projet. Il sera compilé dans le cadre de ce projet. Ensuite, tu vas me dire que le .h est compilé lui 3 fois. En fait, oui et non. Le .h est compilé à chaque fois qu'un .cpp fait un include de cet en-tête. Et ce, que le .cpp implémentant l'en-tête soit dans le même projet ou non.
    Pour ce qui est de la compilation, pour moi je le comprend comme ca:
    visual produit un .lib de l'ensemble des .cpp du projet. Un autre projet utilise ce .lib par le "Autre repertoire include" et les .h de la lib.
    Ensuite il mets soit le .lib dans une dll si on le veux dynamique, soit en statique et alors il est ajouté à l'exe.

    Citation Envoyé par 3DArchi Voir le message
    Le .h est compilé à chaque fois qu'un .cpp fait un include de cet en-tête. Et ce, que le .cpp implémentant l'en-tête soit dans le même projet ou non.
    Pourtant, quand on fait un include il ajoute le.h "virtuellement" au dessus .cpp... il remplace le #include "monheader.h" par son contenu. Donc il n'est compilé que 1 seule fois.
    Par contre, l'ajout en static me semble dire qu'il l'ajoute 3 fois dans le projet final en incluant le .lib au début de la compilation du projet dont il dépend.
    en gros, on a un exe au final qui se partionne de la manière suivante:
    [debut fichier exe] liblogger | libopératoire | projet executable [fin fichier exe]
    Enfin moi c'est comme ca que je le voit depuis le début.

    Un peu comme GCC fait, il crée des fichier .GCH qui sont les cpp compilé et assemble tout leur contenu dans l'ordre d'include pour former l'executable...

    Citation Envoyé par 3DArchi Voir le message
    des en-têtes pré-compilés. [...] via un en-tête adéquat
    Oui je vois.

  9. #9
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 50
    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 Lucyberad Voir le message
    Ensuite il mets soit le .lib dans une dll si on le veux dynamique, soit en statique et alors il est ajouté à l'exe.
    Non : une librairie statique n'est pas une librairie dynamique, et il ne faut pas confondre une librairie statique "normale" avec une librairie d'importation d'une DLL... Même si elles ont la même extension, ça n'a absolument rien à voir.

    La grosse différence entre les deux, c'est qu'une librairie statique n'a pas de phase d'édition de liens : tu peux y mettre ce que tu veux, y compris des symboles indéfinis et n'avoir absolument aucune erreur / warning lorsque tu la génères.
    Inversement, une DLL (ou un exécutable) possèdent une phase d'édition de liens, qui va te gueuler dessus si des symboles sont manquants, ce qui arrive en cas d'erreur dans le code bien sûr mais aussi en cas d'oubli d'une librairie statique dans les dépendances du projet.

    C'est pour ça que l'interdépendance de librairies statiques ne pose en général aucun problème de compilation, alors que l'interdépendance de DLL est parfois impossible à résoudre sans passer par des projets (et DLL) intermédiaires.

    Pour info, le linker de Visual et celui de GCC fonctionnent de façon radicalement différente :
    • Visual parcourt la liste des symboles MANQUANTS, et fouille dans TOUTES les librairies fournies pour trouver ledit symbole.
      Cela peut aboutir à des conflits lorsque le symbole est trouvé plus d'une fois.
      Avantage, l'ordre des librairies n'a aucune importance tant qu'il n'y a pas de conflits.
    • GCC parcourt la liste des LIBRAIRIES, et cherche à placer CHAQUE symbole de la librairie dans la liste des symboles manquants.
      Cela peut aboutir à un problème en cas d'interdépendance, où l'on devra ajouter une librairie deux fois pour résoudre toutes les erreurs de link.
      Par contre, on est certain de la fonction qui sera linkée en fonction de l'ordre des librairies, qui est bien sûr crucial de façon générale avec GCC.

  10. #10
    Membre du Club
    Profil pro
    Inscrit en
    Février 2007
    Messages
    73
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 73
    Points : 69
    Points
    69
    Par défaut
    Ok, je commence à en avoir une idée précise (et contractuelle).
    Merci des infos et de l'aide !

  11. #11
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 379
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 379
    Points : 41 575
    Points
    41 575
    Par défaut
    Et j'ajoute: Si tu as un singleton dans une bibliothèque statique et que cette bibliothèque statique est utilisée par 1 Exe et 2 DLL, alors ton singleton sera instancié trois fois lorsque tu exécuteras ton programme.

    Si tu veux que ton singleton soit vraiment global au processus, tu dois le mettre dans une DLL et non une bibliothèque statique.

  12. #12
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 50
    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 Médinoc Voir le message
    Si tu veux que ton singleton soit vraiment global au processus, tu dois le mettre dans une DLL et non une bibliothèque statique.
    Ou construire un singleton "système", en utilisant par exemple de la mémoire partagée... Ce qui est en général au delà des besoins courants.

  13. #13
    Membre du Club
    Profil pro
    Inscrit en
    Février 2007
    Messages
    73
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 73
    Points : 69
    Points
    69
    Par défaut
    Citation Envoyé par Mac Lak Voir le message
    Ce qui est en général au delà des besoins courants.
    Oui, je pense aussi que cette séparation en 3, je peux la mettre à profit plutôt que de lutter contre elle ^^.

Discussions similaires

  1. Réponses: 0
    Dernier message: 15/12/2010, 16h33
  2. Réponses: 2
    Dernier message: 13/12/2010, 12h14
  3. Réponses: 0
    Dernier message: 19/10/2009, 10h46
  4. portée variable static java entre 2 contextes
    Par c+cool dans le forum Tomcat et TomEE
    Réponses: 2
    Dernier message: 20/04/2009, 09h57
  5. Accessibilité d'un jar au sein d'un projet web
    Par T`lash dans le forum Glassfish et Payara
    Réponses: 1
    Dernier message: 08/03/2008, 07h44

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