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

Affichage des résultats du sondage: Quelle solution vous parais la mieux ?

Votants
11. Vous ne pouvez pas participer à ce sondage.
  • Solution 1

    3 27,27%
  • Solution 2

    8 72,73%
Langage C++ Discussion :

Inclusions circulaires, Comment BIEN faire ?


Sujet :

Langage C++

  1. #1
    Membre à l'essai
    Inscrit en
    Avril 2009
    Messages
    15
    Détails du profil
    Informations personnelles :
    Âge : 36

    Informations forums :
    Inscription : Avril 2009
    Messages : 15
    Points : 16
    Points
    16
    Par défaut Inclusions circulaires, Comment BIEN faire ?
    Bonjour à tous,

    Ma question est un peu particulière, et ressemble a si méprendre a "Bonnet blanc ou Blanc bonnet ?"

    En effet lors de la création d'header, il arrive de faire des référances circulaires. On les bloquent par des #define et #ifndef et on ajoute des class ; pour les définitions. La question n'est pas là.

    Imaginons cette exemple :

    J'ai une classe A et une classe B, les deux se connaissent (j'ai donc besoin d'un include de A dans B et vise versa)

    Deux solutions s'oppose:

    Solution 1 : tout dans les Headers

    Code A.h : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    #ifndef __A_H
    #define __A_H
     
    #include "B.h"
     
    Class B;
     
    Class A {
       private :
          B * __b;
    };
     
    #endif  // __A_H
    Code A.cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    #include "A.h"
     
    /*code vide pour la demo*/
    Code B.h : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    #ifndef __B_H
    #define __B_H
     
    #include "A.h"
     
    Class A;
     
    Class B {
       private :
          A * __a;
    };
     
    #endif  // __B_H
    Code B.cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    #include "B.h"
     
    /*code vide pour la demo*/

    Avantages :
    • Pas de pollution de .h dans les .ccp (seullement le .h reliant le .cpp)
    • Déclaration des Class X et des includes (.h) dans le même fichier


    Solution 2 : un peu partout

    Code A.h : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    #ifndef __A_H
    #define __A_H
     
    Class B;
     
    Class A {
       private :
          B * __b;
    };
     
    #endif  // __A_H
    Code A.cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    #include "A.h"
    #include "B.h"
     
    /*code vide pour la demo*/
    Code B.h : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    #ifndef __B_H
    #define __B_H
     
    Class A;
     
    Class B {
       private :
          A * __a;
    };
     
    #endif  // __B_H
    Code B.cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    #include "B.h"
    #include "A.h"
     
    /*code vide pour la demo*/

    Avantages :
    • J'en cherche toujours ...



    Bien évidement dans les deux solutions si il y a des référancements non-circulaire à faire ils sont mis dans le .h et non dans le .cpp.


    Conclusion :

    Personnelement je trouve la solution 1 bien meilleur, mais on m'a dit "non, c'est pas bien faut coder la solution 2", et à la question "Pourquoi ?" je n'obtiens qu'un "Parce que c'est comme ca !" (pédagogie quand tu nous tiens ... )
    Je cherche donc déséperement à connaitre le fin mot de l'histoire et savoir si ma facon de faire (solution 1) est mauvaise et pourquoi ?

    Me doutant que c'est une question "Bonnet blanc ou Blanc bonnet ?" je veux savoir si l'un est plus efficace que l'autre, si l'un pose problème (norme ISO a tout hasard etc ...)


    Merci d'avance

    Marge

  2. #2
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Citation Envoyé par Marge Voir le message
    Personnelement je trouve la solution 1 bien meilleur, mais on m'a dit "non, c'est pas bien faut coder la solution 2", et à la question "Pourquoi ?" je n'obtiens qu'un "Parce que c'est comme ca !" (pédagogie quand tu nous tiens ... )
    Bonjour,
    La vrai raison, c'est que ta solution n°1 est MAL sans compter d'être VRAIMENT PAS BIEN.


    Ok, Ok, je rigole.

    EDIT : (j'ai oublié un bout ici )
    La différence entre ta solution n°1 et la solution n°2 c'est que dans l'une les includes sont dans les .h et dans l'autre dans les .cpp. La deuxième est meilleure, car elle permet de réduire les temps de compilations en évitant de polluer les .h avec du superflu.

    Bon... un exemple sera surement plus parlant.
    (Dans tout le code qui vient, je n'ai pas mis les #ifndef...#endif, et j'ai utilisé des struct pour gagner en place.)

    On a trois classes, A, B et C.

    A possède une fonction membre qui prend une référence constante sur B. (ça pourrait être un pointeur)
    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
     
    // A.h
    struct B; // déclaration anticipée
    struct A
    {
       void fooA(const B& b) const;
    };
     
    //A.cpp
    #include "A.h"
    #include "B.h"
    #include <iostream>
     
    void A::fooA(const B& b) const
    {
       std::cout << "A\n";
       b.fooB();
    }
    B possède une donnée membre qui est un pointeur sur C. (ça pourrait être une référence)
    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
     
    // B.h
    struct C; // déclaration anticipée
    struct B
    {
       B(C* c);
       void fooB() const;
       C* c_;
    };
     
    //B.cpp
    #include "B.h"
    #include "C.h"
    #include <iostream>
     
    B::B(C* c):c_(c)
    {
    }
     
    void B::fooB() const
    {
       std::cout << "B\n";
       c_->fooC();
    }
    C n'a aucune relation particulière avec les deux autres classes.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    // C.h
    struct C
    {
       void fooC() const;
    };
     
    //C.cpp
    #include "C.h"
    #include <iostream>
    void C::fooC() const
    {
    	std::cout << "C\n";
    }
    Enfin le main
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    #include "A.h"
    #include "B.h"
    #include "C.h"
    int main()
    {
       C* c = new C;
       B b(c);
       A a;
       a.fooA(b);
       return 0;
    }
    Qui affiche "A, B, C".

    En résumé, la classe A a besoin de B et B a besoin de C, par contre A et C n'ont pas connaissance l'une de l'autre.

    EDIT : Ok, tu as corrigé entre temps en rajoutant des pointeurs, le paragraphe qui suit tombe à l'eau.
    Le point central, c'est que le header de A déclare une fonction prenant une référence sur B, et le header de B déclare un pointeur membre sur C. Donc, le compilateur n'a pas besoin de connaitre la déclaration complète de C dans le header de B et n'a pas besoin non plus de la définition complète de B dans le header de A. Il lui suffit d'une déclaration anticipée pour savoir qu'il existe un type nommé "C" et un type nommé "B".

    Maintenant, imagine que l'on modifie C.h, par exemple pour rajouter une fonction ou un membre.

    C.h modifié => il faut recompiler C.cpp
    C.h modifié => B.cpp modifié => il faut recompiler B.cpp

    Par contre, imagine maintenant qu'on applique la solution "tout dans les header". On aurait A.h qui inclut B.h et B.h qui inclut C.h. Résultat :

    C.h modifié => il faut recompiler C.cpp
    C.h modifié => B.h modifié => il faut recompiler B.cpp
    B.h modifié => A.h modifié => il faut recompiler A.cpp

    Au final, une modification dans le header de C entrainerait une recompilation de A, qui n'a pourtant rien à voir.

    Voilà pourquoi l'on préconise d'utiliser les déclarations anticipées dès que possible et de n'inclure que le minimum indispensable dans les headers. Si l'on applique la solution "tout dans les header" partout dans son code, alors le moindre changement va se répercuter en cascade et c'est le monde entier qui recompile.


    Une anecdote pour finir !

    Lors de mon premier stage en entreprise, j'ai eu affaire pour la première fois à un programme C++ vraiment immense. Plusieurs millions de lignes de code, des milliers de fichiers .h et .cpp, des dizaines de programmeurs ayant travaillé dessus depuis environ 5 ans. Et personne n'avait vraiment fait attention à ce problème de déclaration anticipée, en pensant surement que c'était "bonnet blanc et blanc bonnet".

    Le résultat, c'est que les temps de compilation étaient vraiment devenues insoutenables. Pourtant, il y avait un système pour faire de la compilation distribuée en parallèle sur une dizaine de machine. (Et j'avoue avoir eu un choc en voyant la première fois le prog de compilation parallèle m'annoncer que ma compilation se faisait à 42 Ghz !)

    Las, la moindre modification entrainait des recompilations héroïques - un bon quart d'heure d'attente à chaque fois ! On devenait fou. Il a fallu qu'un des programmeurs de l'équipe stoppe toute activité pendant quelques jours, fasse un checkout monstrueux de l'intégralité du code source, puis épluche un par un tous les headers pour rajouter des déclarations anticipées partout où c'était possible.

    Une fois toute ses modifications validées, le temps de compilation moyen est brutalement tombé sous les 5 minutes.

  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
    Solution 1 pour ma part : quand j'inclus un .H, j'entends qu'il soit "complet". J'ai horreur des .H partiels qui réclament N autres .H pour pouvoir fonctionner, et où l'ordre d'inclusion va jouer fortement. Sans compter que, parfois, tu n'as l'erreur qu'au moment du link !
    Au moins, si c'est dans ton .H à toi, l'ordre d'inclusion est déjà correct, et tu peux alors aussi avoir des inclusions automatiques de librairies statiques via les .H si ton compilateur le permet (Visual possède un #pragma à cet effet, par exemple). C'est très pratique pour celui qui utilise tes modules.

    Si le temps de compilation est trop long, il faut :
    • Vérifier qu'il n'y a pas de multiples inclusions inutiles (un graphe de dépendances des fichiers te le montrera, Doxygen en génère de très utiles par exemple).
    • Vérifier que les .H ne recensent bien que le strict minimum en inclusions : juste ce qu'il faut pour compiler un programme minimaliste incluant juste une instance de la classe principale déclarée. Un .H qui incluerait "stdio.h", par exemple, n'est sûrement pas "minimal" côté inclusions... En ce sens, je suis totalement d'accord avec Arzar.
    • Vérifier qu'il n'y a pas trop de choses dans les .H : inutile par exemple d'y mettre les constantes privées, elles doivent être dans un .H séparé utilisé exclusivement par le .CPP correspondant.
    • Vérifier l'arbre d'inclusion des entêtes, et chercher s'il n'y a pas un endroit intéressant pour faire des entêtes précompilés.
    • Séparer au maximum les entêtes "privés" des entêtes "publics", de façon à limiter l'impact d'une modification interne sur le reste du projet. Une modification "privée" d'un module ne devrait JAMAIS provoquer la recompilation en chaîne de l'intégralité du projet !
    • Au besoin, passer par des classes abstraites, ou du masquage d'implémentation : un module réellement modulaire est rarement limité à un seul fichier CPP et un seul entête, je dirais que c'est le plus souvent un fichier .CPP, un .H "public" et un .H "privé" pour assurer un maximum d'indépendance. Je fais en général inclure l'entête public par l'entête privé, pour ma part, le .CPP n'incluant alors que l'entête privé.


    En espérant t'avoir été utile.
    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
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Points : 4 637
    Points
    4 637
    Par défaut
    Autant je te rejoins totalement sur le fait qu'un header doit être indépendant et inclure tout ce dont il a besoin.

    Autant je ne suis pas d'accord avec le choix systématique de la solution 1. Surtout que c'est contradictoire avec un de tes points suivants, à savoir :

    Citation Envoyé par Mac LAK Voir le message
    Vérifier que les .H ne recensent bien que le strict minimum en inclusions : juste ce qu'il faut pour compiler un programme minimaliste incluant juste une instance de la classe principale déclarée. Un .H qui incluerait "stdio.h", par exemple, n'est sûrement pas "minimal" côté inclusions... En ce sens, je suis totalement d'accord avec Arzar.
    En effet, dans le cas présenté ici, seule la déclaration anticipé de la seconde classe (class B est nécessaire dans le header, pas l'inclusion du header complet (#include "B.h").

    Pour revenir sur le découpage des modules entre header publics et header privés, il est parfois possible et utile d'aller plus loin en créant parmi les header publics des header ne prenant en charge que les déclarations anticipées et justement destinés typiquement à être inclus dans d'autres header comme ici.

  5. #5
    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 gl Voir le message
    En effet, dans le cas présenté ici, seule la déclaration anticipé de la seconde classe (class B est nécessaire dans le header, pas l'inclusion du header complet (#include "B.h").
    Pas forcément contradictoire, cela dépend en fait de la classe "maître" et de l'existence d'une telle classe... Je n'ai par contre peut-être pas assez bien expliqué ma notion de "privé" et "public".

    1. Si l'utilisateur ne va utiliser QUE la classe A, par exemple, il faut au maximum tenter de masquer la classe B, y compris en utilisant une classe mère commune (vide et/ou abstraite au besoin) déclarée dans "A.h" afin de masquer son implémentation (et vive le "dynamic_cast<B*>" par la suite...).
    2. Si l'utilisateur a besoin des deux classes tout le temps, inclure un seul des entêtes règlera le problème.
    3. S'il n'a besoin que de la classe B, reprendre le premier cas et inverser les lettres...


    Citation Envoyé par gl Voir le message
    Pour revenir sur le découpage des modules entre header publics et header privés, il est parfois possible et utile d'aller plus loin en créant parmi les header publics des header ne prenant en charge que les déclarations anticipées et justement destinés typiquement à être inclus dans d'autres header comme ici.
    Sauf que s'il y a déclaration anticipée, à un moment où à un autre, il va bien falloir l'implémenter, cette fameuse classe "B"... Et donc inclure son entête, ce qui serait "mal" si l'utilisateur doit le faire manuellement dans son code... Surtout s'il n'a pas, à priori, besoin de la classe B !

    Pour détailler un peu plus :
    • Si les deux classes sont "publiques", la solution 1 est très bien.
    • Si l'une des classes est "privée", alors le fichier d'entête de la classe "publique" doit absolument masquer l'existence de la classe "esclave".


    Dans les deux cas, j'ai expliqué ce qu'il faudrait faire, même si ce n'est pas forcément toujours simple à faire bien entendu. L'utilisation d'une classe mère commune, vide et/ou abstraite est la plus simple, même si ce n'est pas forcément la plus "propre"...
    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

  6. #6
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Citation Envoyé par Mac LAK Voir le message
    Solution 1 pour ma part : quand j'inclus un .H, j'entends qu'il soit "complet". J'ai horreur des .H partiels qui réclament N autres .H pour pouvoir fonctionner, et où l'ordre d'inclusion va jouer fortement. Sans compter que, parfois, tu n'as l'erreur qu'au moment du link !
    Tu pourrais préciser ? Je ne comprends pas trop ce que tu veux dire par .h partiels

    Pourquoi la solution n°2 entrainerait des problèmes d'ordre d'inclusion ou des inclusions redondantes pour l'utilisateur ?

  7. #7
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Points : 4 637
    Points
    4 637
    Par défaut
    Citation Envoyé par Mac LAK Voir le message
    Si l'utilisateur ne va utiliser QUE la classe A, par exemple, il faut au maximum tenter de masquer la classe B, y compris en utilisant une classe mère commune (vide et/ou abstraite au besoin) déclarée dans "A.h" afin de masquer son implémentation (et vive le "dynamic_cast<B*>" par la suite...).
    L'utilisation de la déclaration anticipée de la classe B donne certes l'information de l'existence de cette classe mais ne donne aucune information sur l'implémentation de cette classe.
    A priori, il n'est pas donc nécessaire de mettre en place une classe commune vide ou d'autre mécanisme.

    Citation Envoyé par Mac LAK Voir le message
    Si l'utilisateur a besoin des deux classes tout le temps, inclure un seul des entêtes règlera le problème.
    Si l'utilisateur a besoin des deux classes, je ne vois pas où se situe le problème de devoir inclure dans le .cpp les deux en-têtes.
    Lorsque tu inclues <iostream> tu ne t'attends pas à ce que <string> soit inclus automatiquement même lorsque tu en as besoin, pourquoi en serait-il différemment ici ?

    Le problème de dépendance inclus par de tels constructions viennent:
    • De devoir respecter un ordre d'inclusion.
    • De devoir inclure dans le .cpp un header dont tu ne te sers pas dans le code mais qui est nécessaire pour un autre header.


    Ces deux problèmes sont réglés par l'utilisation d'une déclaration anticipée dans le header.

    Citation Envoyé par Mac LAK Voir le message
    Sauf que s'il y a déclaration anticipée, à un moment où à un autre, il va bien falloir l'implémenter, cette fameuse classe "B"... Et donc inclure son entête, ce qui serait "mal" si l'utilisateur doit le faire manuellement dans son code... Surtout s'il n'a pas, à priori, besoin de la classe B !
    Oui la classe B doit être implémenter, elle le sera dans les fichiers .cpp correspondant, certainement pas dans le code de l'utilisateur.

    De même, le header de la classe B sera probablement inclus dans les fichiers sources de la classe A mais là encore ça ne concerne pas l'utilisateur.

    Si l'utilisateur a besoin de manipuler la classe B, je ne vois pas de problème à ce qu'il inclus les header de la classe B.

    S'il manipule uniquement la classe A, dans ce cas effectivement, il n'a pas à devoir inclure inclure les headers de B, mais la déclaration anticipée dans les headers de A suffit pour l'exemple fourni.

    Le but est que le header de A soit autonome, pas qu'il inclut des headers qu'il ne lui sont pas utiles mais qui pourrait éventuellement servir dans le code utilisateur.
    Inclure le header de B dans celui de A alors que la déclaration anticipée de B est suffisante, apporte un couplage trop important entre A et B qui risque de provoquer plus de problème que d'en résoudre.

  8. #8
    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
    Bonsoir,
    Je suis de ceux qui préfère la solution 2. Outre le problème de temps de compilation présenté par Arzar, il y a à mon avis un moindre couplage entre les différentes classes avec cette solution. En effet, imagine la classe suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    class AImpl;
    class A
    {
    public:
    protected:
    // jamais d'utilisation de AImpl dans l'interface publique ou protégée
    private:
    AImpl *p_impl;
    };
    AImpl est un détail d'implémentation de A et n'a pas à être connue des autres classes utilisant A. Or la première solution, en imposant d'inclure un 'AImpl.h' oblige les autres classes à connaître des choses dont elles n'ont que faire. En effet, AImpl n'intervenant que dans la partie privée de A, elles n'y ont aucun accès. Mais la solution 1 leur impose de connaître ce bidule qui ne leur sert à rien.

  9. #9
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    Personnellement, je suis plutôt du genre à n'utiliser que des fichiers d'en-tête aussi minimalistes que possible.

    Je vois principalement deux bonnes raisons à cela:

    La première, c'est que cela nous évite de recompiler des fichiers qui *semblent* avoir été modifiés du seul fait que l'on a modifié un fichier d'en-tête (qui, de surcroit, s'avère être complètement inutile pour la classe que l'on implémente) qui est inclus par le jeu des inclusions en cascade

    Sur les projets d'importance moyenne à grande, cela peut faire gagner énormément de temps sur la compilation (cf l'exemple cité par Arzar)

    La deuxième tient principalement au fait que rend les dépendances inter modules beaucoup plus explicites, ce qui contribue au repérage rapide d'éventuels problèmes de conception et qui limite de fait l'impact d'éventuelles modifications en vue de les résoudre.

    En effet, la déclaration anticipée n'a qu'un impact tout à fait mineur sur la compilation: elle permet au compilateur de savoir qu'il existe un symbole donné.

    Si nous n'utilisons jamais le symbole en question (comprenez: si nous n'essayons jamais d'accéder au contenu de ce symbole), le compilateur ne s'en portera pas plus mal, et code processeur résultant ne sera absolument pas altéré par la présence de cette déclaration anticipée.

    Par contre, si l'on essaye d'accéder à n'importe quelle partie de l'interface publique de ce symbole, le compilateur se plaindra de ne pas disposer de la définition complète de celui-ci, du moins, si nous n'avons pas inclus le fichier d'en-tête dans lequel la définition est donnée.

    Cette plainte est le moment idéal pour se poser la question de l'utilité de revoir la délégation des tâches et la manière dont les différents modules s'imbriquent les uns dans les autres
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  10. #10
    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 Arzar Voir le message
    Tu pourrais préciser ? Je ne comprends pas trop ce que tu veux dire par .h partiels
    Exemple caractéristique :
    Code .H partiel : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #ifndef PARTIAL_H
    #define PARTIAL_H
     
    // Code d'erreur des fonctions.
    #define ERROR_VALUE UINT_MAX
     
    // Reste du .H
     
    #endif // Anti-inclusion
    Ce fichier .H ne compilera JAMAIS si tu n'as pas, avant lui, inclus "<limits.h>" dans le CPP qui va l'utiliser.

    Rajouter, avant le #define, un simple "#include <limits.h>" lève le souci, et rend le .H "complet".

    Citation Envoyé par Arzar Voir le message
    Pourquoi la solution n°2 entrainerait des problèmes d'ordre d'inclusion ou des inclusions redondantes pour l'utilisateur ?
    Dans l'exemple ci-dessus, "<limits.h>" devra être inclus AVANT ton propre entête, sinon ça ne compilera pas. L'ordre d'inclusion est donc important entre ces deux fichiers.

    Citation Envoyé par gl Voir le message
    L'utilisation de la déclaration anticipée de la classe B donne certes l'information de l'existence de cette classe mais ne donne aucune information sur l'implémentation de cette classe.
    Le problème est que tu déclares alors une classe (non implémentée, certes), mais référencée uniquement via un pointeur sur elle (OK, ça résoud le problème de taille).
    Ceci étant dit, notamment via l'utilisation de templates, tu peux parfois avoir de sales surprises avec ça si jamais le symbole apparaît de façon "publique" alors qu'il doit être totalement privé.
    Pour ma part, si c'est totalement privé, c'est aussi totalement invisible pour l'utilisateur final et je masque jusqu'à l'existence de la classe en question. Cela évite les questions, la documentation qui doit dire au minimum "pas touche", etc.

    Citation Envoyé par gl Voir le message
    Si l'utilisateur a besoin des deux classes, je ne vois pas où se situe le problème de devoir inclure dans le .cpp les deux en-têtes.
    Imagines que la classe "A" soit en fait nommée "CMainProcess" et la classe "B" "CSlaveWorkerThreaded"... Si l'utilisateur utilise presque exclusivement la première classe ("maître"), et que la seconde est réservée à des cas particuliers peu courants mais pas inexistants pour autant, l'utilisateur peut alors se contenter d'inclure l'entête principal "CMainProcess.h" au lieu d'inclure les deux.

    Citation Envoyé par gl Voir le message
    Lorsque tu inclues <iostream> tu ne t'attends pas à ce que <string> soit inclus automatiquement même lorsque tu en as besoin, pourquoi en serait-il différemment ici ?
    Ce qui est "standard" et commun pour les entêtes C/C++ est une chose... L'utilisation d'une librairie spécifique en est une autre, et autant le faire "simple".

    Citation Envoyé par gl Voir le message
    Oui la classe B doit être implémenter, elle le sera dans les fichiers .cpp correspondant, certainement pas dans le code de l'utilisateur.
    Si la classe B est privée, et donc non-exportée de la DLL par exemple, tu fais comment ?

    Citation Envoyé par gl Voir le message
    Si l'utilisateur a besoin de manipuler la classe B, je ne vois pas de problème à ce qu'il inclus les header de la classe B.
    Mais seulement s'il doit la manipuler. Sinon, il ne devrait même pas connaître son existence.

    Citation Envoyé par gl Voir le message
    S'il manipule uniquement la classe A, dans ce cas effectivement, il n'a pas à devoir inclure inclure les headers de B, mais la déclaration anticipée dans les headers de A suffit pour l'exemple fourni.
    Tu peux toujours avoir des cas vicieux qui finissent par déréférencer un pointeur d'une classe déclarée de façon anticipée... S'il manipule uniquement A, "B" ne doit jamais être visible.

    Citation Envoyé par gl Voir le message
    Le but est que le header de A soit autonome, pas qu'il inclut des headers qu'il ne lui sont pas utiles mais qui pourrait éventuellement servir dans le code utilisateur.
    Inclure le header de B dans celui de A alors que la déclaration anticipée de B est suffisante, apporte un couplage trop important entre A et B qui risque de provoquer plus de problème que d'en résoudre.
    Autonome, oui, on est bien d'accord là-dessus je pense, on pinaille sur la manière de le faire plus que sur le principe.
    Pour moi, une classe que l'utilisateur n'a pas à manipuler ne doit pas être visible dans les entêtes publics. Sinon, un entête maximum à inclure par classe utilisée, l'ordre d'inclusion des divers .H publics du module ne devant avoir AUCUNE importance.

    Citation Envoyé par koala01 Voir le message
    La première, c'est que cela nous évite de recompiler des fichiers qui *semblent* avoir été modifiés du seul fait que l'on a modifié un fichier d'en-tête (qui, de surcroit, s'avère être complètement inutile pour la classe que l'on implémente) qui est inclus par le jeu des inclusions en cascade
    Ca, c'est le plus gros problème : savoir quoi mettre comme inclusions dans un entête, et surtout, quoi mettre dans les entêtes "racine" définissant des propriétés communes à tout le projet !

    Le cas immonde : un gros "definitions.h" de 500 lignes qui référence les trois quarts des entêtes standards, ainsi que toutes les constantes et types du projet... Le truc immonde, faudrait crucifier ceux qui pondent un truc pareil.

    Le cas idéal : quelques constantes / types maximum pour chaque entête "racine", mais par contre des dizaines d'entêtes "racine" si besoin.

    Dans tous les cas, vérifier la possibilité de faire des entêtes précompilés au niveau de ces entêtes "racine", car ils seront inclus par plus ou moins tous les modules du projet.

    Citation Envoyé par koala01 Voir le message
    La deuxième tient principalement au fait que rend les dépendances inter modules beaucoup plus explicites, ce qui contribue au repérage rapide d'éventuels problèmes de conception et qui limite de fait l'impact d'éventuelles modifications en vue de les résoudre.
    Justement, tiens : je préfère les dépendances "publiques" plutôt que de me rendre compte, au link, que j'ai besoin d'une librairie (statique ou dynamique) qui n'avait à priori absolument rien à voir avec mon programme, mais qui est requise à cause d'une dépendance interne... Le cas est horriblement fréquent avec les librairies statiques.

    Citation Envoyé par koala01 Voir le message
    Si nous n'utilisons jamais le symbole en question (comprenez: si nous n'essayons jamais d'accéder au contenu de ce symbole), le compilateur ne s'en portera pas plus mal, et code processeur résultant ne sera absolument pas altéré par la présence de cette déclaration anticipée.
    Tout tient dans ton premier mot : SI... Les templates ont parfois un comportement bizarre et déréférencent les pointeurs qu'on leur donne. Habituellement, ça ne pose jamais de souci (vu qu'on en connait le type, il a servi à initialiser le template), mais parfois... Autant être prudent, surtout dans un entête public.
    Dans un entête privé, par contre, on s'en fiche : on est dans "notre" module, on sait ce que l'on fait (enfin... on est censé savoir ! ), et on peut donc y aller tranquillement en général.

    Citation Envoyé par koala01 Voir le message
    Cette plainte est le moment idéal pour se poser la question de l'utilité de revoir la délégation des tâches et la manière dont les différents modules s'imbriquent les uns dans les autres
    Ce qui rejoint ce que je disais : pourquoi laisser un symbole à priori privé "visible" dans une interface publique, même sous forme de déclaration anticipé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

  11. #11
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Mac LAK Voir le message
    Justement, tiens : je préfère les dépendances "publiques" plutôt que de me rendre compte, au link, que j'ai besoin d'une librairie (statique ou dynamique) qui n'avait à priori absolument rien à voir avec mon programme, mais qui est requise à cause d'une dépendance interne... Le cas est horriblement fréquent avec les librairies statiques.
    Justement, la plainte aura lieu à la compilation, et non à l'édition des liens (dés que le compilateur rencontrera un ptr->doSomething(); par exemple)
    Tout tient dans ton premier mot : SI...
    Si tu veux, je reformule en
    Tant que tu te contente de manipuler un pointeur (sans tenter d'accéder à l'interface publique que l'objet pointé), tu n'a aucun besoin de disposer de l'interface publique...

    Dés le moment où tu dois accéder à l'interface publique, c'est visiblement qu'il est temps de te poser la question de la bonne délégation des taches...
    Nous avons en effet souvent un desing proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class holder;
    class Object
    {
        public:
            /* une interface publique quelconque 
             * qui permet, entre autre, de récupérer le contenant dans lequel 
             * il se trouve
             */
            /* const */ Holder* parent() /*const */{return mpar;} 
        private:
            Holder *mpar;
    };
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class Object;
    class Holder
    {
        public:
            void registerObject(Object * o /*,autres parametres intéressant */);
            void eraseObject(Object *o);
            /* pourrait prendre la forme de */
            void eraseObject(/* parametres utile à l'identification de l'objet */);
            /* const */Object* getItem(/* parametres éventuels */) /* const*/;
            /*reste de l'interface publique */
        private:
           /* ce pourrait être n'importe quel conteneur */
           std::vector<Object*> mitems;
    };
    Si, pour implémenter le reste de l'interface publique du conteneur (en dehors d'un éventuel modèle proche du modèle composite), tu en viens à devoir inclure l'en-tête de Object, c'est peut être qu'il est temps d'envisager de revoir la délégation des tâches

    Par contre, lorsque l'on en arrive à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int main()
    {
        Holder h;
        h.registerObject(new object());
        /*...*/
        Object* item=h.getItem();
        itemp->doSomething();
        /*...*/
        return 0;
    }
    il n'est pas illogique de forcer à inclure les deux fichiers d'en-tête, étant donné que l'utilisateur veut effectivement disposer de l'interface publique des deux types particuliers
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  12. #12
    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 koala01 Voir le message
    Justement, la plainte aura lieu à la compilation, et non à l'édition des liens (dés que le compilateur rencontrera un ptr->doSomething(); par exemple)
    Je pense surtout aux dépendances internes planquées, par exemple avoir besoin en interne de la ZLib et ne pas le montrer "publiquement"... Ce qui fera qu'il n'y aura aucune inclusion publique d'un entête ZLib (normal, c'est "interne"), mais au link, il va demander la ZLib et tu ne sauras pas forcément pourquoi ou comment elle est requise, surtout si tu n'as rien demandé publiquement en terme de compression !
    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

  13. #13
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Mac LAK Voir le message
    Je pense surtout aux dépendances internes planquées, par exemple avoir besoin en interne de la ZLib et ne pas le montrer "publiquement"... Ce qui fera qu'il n'y aura aucune inclusion publique d'un entête ZLib (normal, c'est "interne"), mais au link, il va demander la ZLib et tu ne sauras pas forcément pourquoi ou comment elle est requise, surtout si tu n'as rien demandé publiquement en terme de compression !
    J'ai l'impression que tu cherche des problèmes là où il n'y a pas lieu d'en avoir et dont la cause principale tient en trois mots :défaut de communication... (jadis j'avais un collègue qui utilisait volontiers le terme de "handicapés de la communication" )

    En effet, de deux choses l'une:

    Soit, tu es l'auteur d'un projet (ou tu fais partie de l'équipe de développement d'un projet), et les dépendances envers les bibliothèques externes doivent être gérées au niveau du projet, et des options de compilation en générale.

    Si les options de compilation viennent à différer d'un poste à l'autre, il est à mon sens grand temps de revoir la comm au sein de l'équipe, ne serait-ce que parce que les dépendances externes devraient avoir été abordées dés l'étape de la conception (ou d'une révision de la conception)

    Tu me diras sans doute qu'il faut encore être dans une équipe dans laquelle la communication passe correctement, et j'admets que ce n'est pas forcément gagné...

    Mais, avec les outils actuels, même si tu ne veux pas parler à ton collègue, la gestion cohérente de la configuration reste relativement facile à mettre en oeuvre (ne serait-ce qu'au niveau des outils de versionning si on utilise des Makefile ou équivalent)

    Soit tu es l'utilisateur d'un projet externe, et les dépendances de ce projet externe envers d'autres bibliothèques font partie des pré-requis d'utilisation...

    Elles doivent donc apparaitre dans la documentation d'installation et dans la documentation utilisateur ( ne serait-ce que sous une forme proche de "attention, la liaison avec telle bibliothèque est requise pour le fonctionnement interne)...

    Bon, tu me diras sans doute qu'il faut encore que la documentation soit complète et à jour, mais c'est aussi cela qui fait la différence de qualité dans le choix d'une bibliothèque externe
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  14. #14
    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 koala01 Voir le message
    J'ai l'impression que tu cherche des problèmes là où il n'y a pas lieu d'en avoir et dont la cause principale tient en trois mots :défaut de communication... (jadis j'avais un collègue qui utilisait volontiers le terme de "handicapés de la communication" )
    Je dirais plutôt : "reprise d'un existant pas forcément développé dans les règles de l'art"...

    Là, je parle de plusieurs dizaines de librairies / modules utilisées conjointement par d'autres dizaines d'exécutables, le tout pour plusieurs plate-formes et quelques centaines de milliers de lignes de code. Bref, pas spécialement un "petit truc" non plus.

    Les dépendances internes (et la "pieuvre" qui va avec dès qu'on utilise une librairie) sont une vraie plaie en général, surtout via des librairies statiques. Et il n'est hélas pas toujours possible d'utiliser des links automatiques de librairies comme le permet Visual C++. Rien à voir avec de la communication, le problème étant que, parfois, plus personne ne sait et qu'une analyse du code est nécessaire.

    Si l'OP est dans un tel cadre, et peut éviter de démarrer une situation qui, d'ici quelques années provoquera un code tentaculaire, c'est tout aussi bien, non ?

    Citation Envoyé par koala01 Voir le message
    Bon, tu me diras sans doute qu'il faut encore que la documentation soit complète et à jour, mais c'est aussi cela qui fait la différence de qualité dans le choix d'une bibliothèque externe
    La documentation est le principal problème, les dépendances étant rarement renseignées, la doc rarement à jour, et c'est encore pire quand le mot "externe" signifie "le bureau d'à côté" : les crédits pour la documentation interne non vendue au client sont, en général, égaux à zéro.
    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

  15. #15
    Expert éminent

    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Février 2007
    Messages
    4 253
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Février 2007
    Messages : 4 253
    Points : 7 618
    Points
    7 618
    Billets dans le blog
    3
    Par défaut
    La seule solution est la 2...

    Inclure "A.h" veut dire: Je vais utiliser A... pas B qui est utilisé par A!
    Ce qui veut dire que A.h est complet... ce qui veut dire, inclure tout le nécessaire pour *utiliser* A, et pas utiliser les objets accessibles par A...

    Après que le source de A fasse une inclusion de B (parcequ'il l'utilise) rien de plus normal, mais c'est *son* probleme.

    Pensez "objet" !
    N'oubliez pas de cliquer sur mais aussi sur si un commentaire vous a été utile !
    Et surtout

  16. #16
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Mac LAK Voir le message
    <snip>
    Là, je parle de plusieurs dizaines de librairies / modules utilisées conjointement par d'autres dizaines d'exécutables, le tout pour plusieurs plate-formes et quelques centaines de milliers de lignes de code. Bref, pas spécialement un "petit truc" non plus.

    Les dépendances internes (et la "pieuvre" qui va avec dès qu'on utilise une librairie) sont une vraie plaie en général, surtout via des librairies statiques. Et il n'est hélas pas toujours possible d'utiliser des links automatiques de librairies comme le permet Visual C++. Rien à voir avec de la communication, le problème étant que, parfois, plus personne ne sait et qu'une analyse du code est nécessaire.
    Oui, mais bon...

    Je vois mal une équipe de développement travailler sur un projet aussi tentaculaire que ce que tu semble présenter sans utiliser un système de compilation automatique (que ce soit intégré à l'environnement de développement ou que ce soit à l'aide de Makefile ou équivalent)...

    Tout comme j'espère pour cette équipe qu'elle utilise, au minimum, un système de gestion de version, et qu'elle veille malgré tout à faire des updates plus ou moins réguliers

    Dans le pire des cas, même si l'ajout d'une dépendance externe n'est pas forcément publié en interne de manière explicite, il provoquera une mise à jour du (des) Makefile (ou tout système équivalent) et l'on peut donc estimer que cet ajout de dépendance sera pris en compte, même si c'est de manière plus ou moins transparente pour l'équipe de développement

    Reste le cas éventuel où l'une des implémentation utilise une bibliothèque tierce différente, mais cela impliquera alors la mise au point d'un système de compilation conditionnelle, en vue, justement, de permettre d'utiliser les bonnes fonctions de la bonne bibliothèque...

    Et la mise à jour du système ce compilation automatique correspondant

    Bref, je persiste et signe dans l'optique que tu cherche un problème où il n'y a normalement pas lieu d'en trouver

    La documentation est le principal problème, les dépendances étant rarement renseignées, la doc rarement à jour, et c'est encore pire quand le mot "externe" signifie "le bureau d'à côté" : les crédits pour la documentation interne non vendue au client sont, en général, égaux à zéro.
    Ne mélangeons pas tout, s'il te plait...

    D'un coté, il y a l'utilisateur d'un projet, qui le récupère d'une manière ou d'une autre (téléchargement sur le site ) et de l'autre, il y a l'équipe de développement du projet.

    Au niveau de l'utilisateur, il y aura, espérons le, vérification des pré-requis nécessaires à l'utilisation lors de l'installation (et donc vérification de la présence de la lib externe machin chose si elle n'est pas fournie dans le "pack" du projet)

    Tout ce que je demande à la limite, c'est de mettre dans la documentation utilisateur une phrase qui peut être aussi simple que:
    Attention, ce projet nécessite la présence de la bibliothèque machin chose (fournie ou non) pour son usage interne, et d'être compilé avec les options de compilation ou d'édition de liens qui vont bien (-lmachinchose)
    Cela m'a pris 45 secondes à écrire, et cela montre à l'utilisateur qu'on le "prend par la main" (et on lui évite au passage la désagréable surprise d'un symbole introuvable)

    Au niveau de l'équipe de développement, maintenant:

    J'espère pour toi et ton équipe que tu dispose, quand même, de documents de conception et de spécifications...

    L'ajout de dépendances externes pour un module particulier dois donc au minimum apparaitre dans un de ces documents... Même si ce n'est qu'un encart bien caché...

    Et, en tout état de cause, cela doit apparaître d'une manière ou d'une autre dans le système d'automatisation de compilation

    Les objections que tu émets en viennent presque à me faire peur de travailler dans ta boite, parce que si:
    • elle travaille sans specs
    • elle travaille sans système de compilation automatique
    • elle travaille sans document de conception
    (que ce soit sans l'un des trois, ou, pire, sans les trois) j'en viens presque à me demander comment elle arrive à ne pas tomber dans l'anarchie la plus totale et à fournir un résultat correct, même en explosant les délais et le budget...

    Alors, je sais effectivement que certaines entreprises font figure "d'handicapés de la communication" en interne et qu'elles n'allouent que peu de crédits dans ce domaine...

    Mais, en toute logique, il doit rester *relativement* simple, pour peu que l'on utilise les outils à notre disposition, d'assurer un minimum de cohésion entre les différents modules d'un projet

    Ceci dit, il serait aussi pas mal d'attirer l'attention de qui de droit dans l'entreprise sur le fait que le temps "perdu" à assurer un bon passage de l'information (et je ne parle pas forcément de l'organisation de réunions) est un investissement qui, à terme, est de nature à limiter grandement les dépassement d'agenda et l'explosion du budget
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  17. #17
    Rédacteur

    Avatar de ram-0000
    Homme Profil pro
    Consultant en sécurité
    Inscrit en
    Mai 2007
    Messages
    11 517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Consultant en sécurité
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2007
    Messages : 11 517
    Points : 50 367
    Points
    50 367
    Par défaut
    Solution 1 pour moi aussi.

    Quand j'inclus un header, je ne veux pas me prendre la tête pour savoir ce qu'il faut inclure avant ni dans quel ordre ni avec quelles définitions à positionner. C'est le boulot du concepteur du header que de faire cela.
    Raymond
    Vous souhaitez participer à la rubrique Réseaux ? Contactez-moi

    Cafuro Cafuro est un outil SNMP dont le but est d'aider les administrateurs système et réseau à configurer leurs équipements SNMP réseau.
    e-verbe Un logiciel de conjugaison des verbes de la langue française.

    Ma page personnelle sur DVP
    .

  18. #18
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par ram-0000 Voir le message
    Solution 1 pour moi aussi.

    Quand j'inclus un header, je ne veux pas me prendre la tête pour savoir ce qu'il faut inclure avant ni dans quel ordre ni avec quelles définitions à positionner. C'est le boulot du concepteur du header que de faire cela.
    La gestions de l'ordre d'inclusion ne devrait effectivement pas être du ressort de l'utilisateur des en-têtes...

    Mais le débat porte sur les inclusions (et les dépendances) croisées, et non sur les inclusions (et les dépendances) que l'on pourrait qualifier de "en cascade"...

    Je persiste dans le fait qu'il n'est finalement pas normal (sauf dans les cas particuliers déjà présentés) de permettre à l'utilisateur d'accéder à une classe autre que celle envers laquelle il a effectivement déclaré une dépendance (par inclusion du fichier d'en-tête correspondant)...

    Du moins, pas sans l'inciter au préalable à se poser des questions concernant l'opportunité d'accéder au contenu de cette classe supplémentaire reçue comme "en bonus" dans le cadre d'une bonne délégation des tâches.
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  19. #19
    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 koala01 Voir le message
    Je vois mal une équipe de développement travailler sur un projet aussi tentaculaire que ce que tu semble présenter sans utiliser un système de compilation automatique (que ce soit intégré à l'environnement de développement ou que ce soit à l'aide de Makefile ou équivalent)...
    Makefiles et projets Visual, oui. On ne va pas non plus taper les commandes de compilation à la main, hein...

    Citation Envoyé par koala01 Voir le message
    Tout comme j'espère pour cette équipe qu'elle utilise, au minimum, un système de gestion de version, et qu'elle veille malgré tout à faire des updates plus ou moins réguliers
    C'est justement là le souci : les conflits de mises à jour / corrections / ajouts... Les dépendances internes posent de gros soucis, car il est déjà arrivé d'avoir des recompilations abusives (inclusions de .H qui n'avaient rien à foutre là), et aussi des recompilations manquantes (la "bonne compilation" d'un bidule dépendait de l'ordre d'inclusion dans le CPP du .H en question...).

    Citation Envoyé par koala01 Voir le message
    Bref, je persiste et signe dans l'optique que tu cherche un problème où il n'y a normalement pas lieu d'en trouver
    Faut le vivre pour le comprendre, je pense...
    Classe privée = classe invisible et inconnue, ça c'est le secret du bonheur...

    Citation Envoyé par koala01 Voir le message
    D'un coté, il y a l'utilisateur d'un projet, qui le récupère d'une manière ou d'une autre (téléchargement sur le site ) et de l'autre, il y a l'équipe de développement du projet.
    Oui, en effet. Sachant que tu passes souvent de l'un à l'autre en fonction des besoins et de la charge, ce qui biaise fortement ta vue "concepteur" et ta vue "utilisateur", car tu fais aussi souvent l'un que l'autre.

    Citation Envoyé par koala01 Voir le message
    Au niveau de l'utilisateur, il y aura, espérons le, vérification des pré-requis nécessaires à l'utilisation lors de l'installation (et donc vérification de la présence de la lib externe machin chose si elle n'est pas fournie dans le "pack" du projet)
    Il ne devrait même pas être autorisé qu'une librairie dépende d'une autre de façon "planquée"... La référence devrait impérativement être publique, ne serait-ce que pour permettre l'utilisation d'un stub si besoin.

    Citation Envoyé par koala01 Voir le message
    Tout ce que je demande à la limite, c'est de mettre dans la documentation utilisateur une phrase qui peut être aussi simple que:
    Quelle documentation ? Le commentaire au fin fond du .CPP ?
    Module interne = pas d'autre doc que les commentaires la plupart du temps... Ou, pire, la doc existe mais c'est la croix et la bannière pour retrouver dans QUEL projet elle était incluse !!!

    Citation Envoyé par koala01 Voir le message
    J'espère pour toi et ton équipe que tu dispose, quand même, de documents de conception et de spécifications...
    Sur le projet complet, oui, bien entendu.

    Sur les modules internes plus ou moins réutilisés à chaque projet, non : pas de crédits pour ça. Donc, soit tu épluches les docs du projet précédent (et tu te fais engueuler, en admettant que tu y aie accès !!), soit tu lis les commentaires du code (enfin... ce qui sert de commentaire) qui incluent rarement ce genre d'information.

    C'est rarement autrement en général : la doc n'est faite que si quelqu'un la paie. Je suis le premier à le déplorer, mais c'est hélas courant.

    Citation Envoyé par koala01 Voir le message
    L'ajout de dépendances externes pour un module particulier dois donc au minimum apparaitre dans un de ces documents... Même si ce n'est qu'un encart bien caché...
    Cela doit être PUBLIC, en gros, et encore mieux : optionnel avec un stub interne en cas d'absence des fonctions.
    Ça, c'est "propre", au moins. Cela demande bien sûr à se décarcasser un peu et à utiliser le linkage DLL JIT, c'est un peu plus difficile à faire en librairie statique mais ça reste faisable.

    Citation Envoyé par koala01 Voir le message
    Alors, je sais effectivement que certaines entreprises font figure "d'handicapés de la communication" en interne et qu'elles n'allouent que peu de crédits dans ce domaine...
    T'as même pas idée à quel point c'est "peu", vu que c'est zéro.

    Citation Envoyé par koala01 Voir le message
    Mais, en toute logique, il doit rester *relativement* simple, pour peu que l'on utilise les outils à notre disposition, d'assurer un minimum de cohésion entre les différents modules d'un projet
    On parie ?

    Citation Envoyé par koala01 Voir le message
    Ceci dit, il serait aussi pas mal d'attirer l'attention de qui de droit dans l'entreprise sur le fait que le temps "perdu" à assurer un bon passage de l'information (et je ne parle pas forcément de l'organisation de réunions) est un investissement qui, à terme, est de nature à limiter grandement les dépassement d'agenda et l'explosion du budget
    Je sais. Cependant, on a en général les points suivants :
    • Le commercial vend au plus juste, quitte à ne pas tenir compte du chiffrage des techniques.
    • Le CP ne regarde que SON projet, aucun autre, et se fiche de plomber les autres projets avec du code "bouillasse" tant que SON projet est dans les budgets.
    • Les financiers / responsables jouent à la roulette avec la maintenance : si jamais il n'y en a pas besoin, c'est tout bénèf, si jamais y'a des bugs, "on avisera".
    • Dans tous les cas, leur montrer les chiffres d'un précédent projet renvoie les réponses "C'était un cas particulier", "On a progressé depuis", "Y'a pas de risques, faut juste adapter le code ('adapter' s'applique par exemple au portage d'une appli Win 3.1 en VB4 vers du Java sous un Linux embarqué, au fait...)", etc. Bref, il n'y a pas pire sourd que celui qui ne veut pas entendre.
    • Les dévs doivent réparer toutes ces conneries (dont ils ne sont pourtant pas responsables), et tenir les délais, d'où une impasse sur tout ce qui est maintenabilité ultérieure d'autant plus grande que le projet est à la bourre, et le code "critique"...



    "Bienvenue dans MON cauchemar !" (c) Freddy Krueger.
    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

  20. #20
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Citation Envoyé par Mac LAK Voir le message
    Les dépendances internes (et la "pieuvre" qui va avec dès qu'on utilise une librairie) sont une vraie plaie en général, surtout via des librairies statiques.
    Nous sommes d'accord. Et les dependances inutiles comme dans le cas present le sont particulierement. Un entete doit etre independant (donc ne pas exiger qu'on ait inclus d'autres entetes avant) et minimal (donc ne pas inclure ce dont on n'a pas besoin). Donc c'est la solution 2.

    Si on a deux classes qui sont tellement liees qu'utiliser une sans utiliser l'autre n'a pas de sens, on les mets dans le meme entete qu'on nomme correctement. C'est la solution non proposee:
    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
     
    // AetB.h
    #ifndef AetB_H
    #define AetB_H
     
    class B;
     
    Class A {
       private :
          B * b_;
    };
     
    class B {
       private :
          A *a_;
    }
    };
    #endif
    (En passant, deux underscores consecutifs, c'est reserve a l'implementation dans tous les contextes; et j'evite d'un utiliser un initial, je ne retiens jamais quand c'est utilisable et quand c'est reserve).
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

Discussions similaires

  1. Comment "bien" faire ses CSS
    Par sliderman dans le forum Mise en page CSS
    Réponses: 11
    Dernier message: 30/06/2008, 20h38
  2. [Debutant] comment bien faire une variable ?
    Par nighthammer dans le forum iReport
    Réponses: 2
    Dernier message: 27/05/2008, 11h56
  3. comment bien faire organiser ses header
    Par DEVfan dans le forum C++
    Réponses: 43
    Dernier message: 29/04/2008, 11h58
  4. Class de mesh, comment bien faire ?
    Par Tosh dans le forum Développement 2D, 3D et Jeux
    Réponses: 8
    Dernier message: 24/04/2007, 12h24
  5. [MS/SQL 2K][Securité][Restauration]Comment bien faire ?
    Par jbat dans le forum MS SQL Server
    Réponses: 3
    Dernier message: 18/04/2007, 11h18

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