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

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

C Discussion :

Usage pour les #include


Sujet :

C

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Mai 2009
    Messages
    13
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2009
    Messages : 13
    Points : 14
    Points
    14
    Par défaut Usage pour les #include
    Bonjour,

    supposons que dans un fichier d'en tête j'ai besoin de foo.h et de bar.h
    Mais que bar.h inclut déjà foo.h (donc je peux me contenter d'inclure seulement bar.h).

    Est-ce que j'inclus les deux ou seulement bar.h ?

    Merci.

  2. #2
    Membre averti Avatar de Jenna
    Inscrit en
    Décembre 2009
    Messages
    272
    Détails du profil
    Informations personnelles :
    Âge : 40

    Informations forums :
    Inscription : Décembre 2009
    Messages : 272
    Points : 339
    Points
    339
    Par défaut
    Pour moi, la règle est assez simple :
    • Si tu as besoin d'un composant de bar.h, tu inclus bar.h
    • Si tu as besoin d'un composant de foo.h, tu inclus foo.h


    Si bar.h a besoin d'un composant de foo.h il doit l'inclure. Un include file devrait être complet et ne devrait forcer l'utilisateur à rechercher ce dont à besoin bar.h pour l'inclure avant.

    De toute façon, si foo.h est inclus 2 fois (par ton module et par bar.h inclus par ton module), cela ne doit pas poser de problème non plus.

  3. #3
    Rédacteur
    Avatar de Franck.H
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Janvier 2004
    Messages
    6 951
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Service public

    Informations forums :
    Inscription : Janvier 2004
    Messages : 6 951
    Points : 12 462
    Points
    12 462
    Par défaut
    +1 ... J'inclus les fichiers qu'il faut quand il faut. De toute manière, si tu inclus le même fichier dans différents headers ou sources, si c'est bien fait (protection contre les inclusions multiples) y'a aucun problème !

  4. #4
    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
    Il y en a pourtant un, de problème : ralentissement pouvant être très sensible de la vitesse de compilation, et pollution assez lourde d'un graphe de dépendance si l'on décide (ou que l'on a l'habitude) d'en utiliser... Ce qui pose d'énormes problèmes d'ailleurs lorsque l'on veut effectuer la séparation d'un module en deux parties, ou simplement analyser les dépendances.

    Pour ma part, c'est plutôt le contraire : je vise à vérifier / m'assurer que l'on n'inclus pas les entêtes de façon inutile et/ou redondante.
    Par contre, je vise également à ce que les entêtes .H soient complets (= ne requièrent pas d'inclusions additionnelles dans un C/CPP qui les utiliseraient) : il est donc hors de question de supprimer une inclusion "comme ça", il faut malgré tout que la chaîne d'inclusion totale soit correcte ET complète.

  5. #5
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 401
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 401
    Points : 23 783
    Points
    23 783
    Par défaut
    Bonjour,

    Citation Envoyé par cirdan_elf Voir le message
    donc je peux me contenter d'inclure seulement bar.h). Est-ce que j'inclus les deux ou seulement bar.h ?
    - Sémantiquement, tu dois inclure le fichier qui définit l'entité que tu utilises ;
    - Techniquement, rien ne garantit la manière dont le fichier inclus sera écrit. D'une plate-forme à l'autre, il se peut très bien que le sous-fichier inclus ne le soit plus. Il faut donc agir comme si tu ne pouvais voir l'intérieur de ce fichier.

    Plus précisément, il n'y a pas de relation hiérarchique entre les *.h comme il y en a dans un arbre de classes objet, par exemple. bar.h « n'implique pas » foo.h. Par ailleurs, la directive #include sert à inclure n'importe quoi, pas forcément des *.h. C'est assez rare de voir autre chose, bien sûr, mais c'est le cas avec les *.xpm, par exemple.

    Pour éviter que le contenu d'un *.h soit redéfini plusieurs fois, on l'encadre généralement avec des macros du genre :

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    #ifndef FOO_H
    #define FOO_H 1
     
    ... [contenu] ...
     
    #endif

    Il te faudra penser à le faire à ton tour si, toi aussi, tu écris une bibliothèque et fournis son *.h.

    Citation Envoyé par Mac LAK Voir le message
    Pour ma part, c'est plutôt le contraire : je vise à vérifier / m'assurer que l'on n'inclus pas les entêtes de façon inutile et/ou redondante. Par contre, je vise également à ce que les entêtes .H soient complets (= ne requièrent pas d'inclusions additionnelles dans un C/CPP qui les utiliseraient) : il est donc hors de question de supprimer une inclusion "comme ça", il faut malgré tout que la chaîne d'inclusion totale soit correcte ET complète.
    Ce n'est pas sa question : il ne s'agit pas de coller des headers classique pour l'hygiène mais, au contraire, de savoir s'il faut volontairement omettre un header requis parce qu'un autre est réputé l'avoir inclus lui-même auparavant. La réponse est « non », pour les raisons exposées ci-dessus.

    Pour les délais de compilation, aucun overhead notable n'est introduit si le fichier est encadré par les bonnes macros. Son interprétation prend fin dès le départ, lorsque la ligne idoine est atteinte. Pour les graphes des dépendances, celles-ci peuvent former un cycle sans que ce soit forcément une erreur. C'est donc à toi (ou à ton logiciel traceur) de conserver une liste de chaque fichier inclus et d'interrompre la progression de la recherche lorsqu'il est fait référence à un fichier de cette liste.

  6. #6
    Expert éminent sénior

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 66
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Points : 17 923
    Points
    17 923
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    Ce n'est pas sa question : il ne s'agit pas de coller des headers classique pour l'hygiène mais, au contraire, de savoir s'il faut volontairement omettre un header requis parce qu'un autre est réputé l'avoir inclus lui-même auparavant. La réponse est « non », pour les raisons exposées ci-dessus.

    Pour les délais de compilation, aucun overhead notable n'est introduit si le fichier est encadré par les bonnes macros. Son interprétation prend fin dès le départ, lorsque la ligne idoine est atteinte. Pour les graphes des dépendances, celles-ci peuvent former un cycle sans que ce soit forcément une erreur. C'est donc à toi (ou à ton logiciel traceur) de conserver une liste de chaque fichier inclus et d'interrompre la progression de la recherche lorsqu'il est fait référence à un fichier de cette liste.

  7. #7
    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 Obsidian Voir le message
    Ce n'est pas sa question : il ne s'agit pas de coller des headers classique pour l'hygiène mais, au contraire, de savoir s'il faut volontairement omettre un header requis parce qu'un autre est réputé l'avoir inclus lui-même auparavant. La réponse est « non », pour les raisons exposées ci-dessus.
    Chose pourtant classiquement faite sur pas mal d'entêtes standards et/ou d'OS... Typiquement, si tu développes sous Windows, je pense que tu auras rarement inclus "winbase.h" et que tu inclus directement "windows.h"...

    Pour le "réputé", il y a aussi le fait d'avoir des entêtes complets sans scories (= TOUS les entêtes requis par l'utilisation du .H, et SEULEMENT ces entêtes). Donc, il n'y a pas de "réputé" : si tu utilises un module XX qui manipule des std::string, il est inutile d'inclure toi-même <string> : le header "XX.h" le fait pour toi, s'il est complet. Et tu peux généraliser ça à toute sous-classe, entité ou type manipulé par la classe que tu as utilisée.
    Si par contre tu "profites" d'une inclusion de <string> via un header qui ne manipule pas publiquement de chaînes, là, c'est effectivement mauvais de conserver cette inclusion : il faudrait la supprimer du header qui ne l'utilise pas.

    Citation Envoyé par Obsidian Voir le message
    Pour les délais de compilation, aucun overhead notable n'est introduit si le fichier est encadré par les bonnes macros.
    L'absence de macros peut (et souvent, va) provoquer des warnings au mieux, des erreurs au pire. Pour le reste, le fichier est malgré tout inclus et passé au préproc, même s'il ne génère que des lignes vides. Si c'est juste un .H à toi de 20 lignes, ce n'est pas trop grave. Si c'est "windows.h" et toute sa tripaille que tu inclus N fois pour rien, ça commence à devenir nettement plus sérieux, et plus tu multiplies ce phénomène, plus l'impact est lourd. Pour ma part, je vois nettement le souci sur les inclusions des entêtes ACE, par exemple : en rajouter "pour rien", c'est un ralentissement sensible de la vitesse de compilation... Surtout que si tu exagères un peu trop, tu finis par avoir besoin de swapper. OK, les fichiers peuvent être dans le cache, mais sur une très (trop ?) grosse partie, tu vas te retrouver avec deux processus qui réclament la RAM : le compilateur pour le fichier en cours de compilation, et le cache pour les maintenir disponibles.

    Fais le test avec cet entête :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    const int TEST_INCLUDE_START = 0 ;
    #ifndef TEST_INCLUDE
    #define TEST_INCLUDE
     
    // Rajouter N lignes vides et/ou copier ce commentaire
     
    #endif
    const int TEST_INCLUDE_END = 1 ;
    Si tu l'inclus plusieurs fois, tu le verras plusieurs fois définies dans le fichier prétraité (-E), et les numéros de ligne vérifiés (et affichés) à chaque inclusion de cet entête. Suivant le compilateur et les réglages, tu auras ou non les lignes vides maintenues dans le fichier prétraité.

    Citation Envoyé par Obsidian Voir le message
    Pour les graphes des dépendances, celles-ci peuvent former un cycle sans que ce soit forcément une erreur. C'est donc à toi (ou à ton logiciel traceur) de conserver une liste de chaque fichier inclus et d'interrompre la progression de la recherche lorsqu'il est fait référence à un fichier de cette liste.
    En l'occurrence, Doxygen n'effectue pas cette coupe (et heureusement, d'ailleurs, ça permet aussi de résoudre des problèmes de complétude des entêtes). Quant aux dépendances cycliques (ou encore pire : inutiles), c'est de toutes façons quelque chose d'assez crasseux pour laquelle il est quand même souhaitable de se poser la question "ai-je correctement fait ma conception de modules ?"...

  8. #8
    Expert éminent sénior

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 66
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Points : 17 923
    Points
    17 923
    Billets dans le blog
    2
    Par défaut
    euh... tu cites C++ et Windows...


    Ayant toujours développé en C pour unixoides, je ne connais pas les pratiques ni C++, ni Win, mais je peux t'assurer que ce n'est pas la pratique recommandée (ni utilisée, voir les sourceforge ou autres archives "avant C++") en C sous unixoides...

    En général, par exemple sur un projet, tu fais un "CommonC.h", qui va inclure les headers généraux dont on a toujours besoin (stdio, stdlib, time, limits, ...), et tu t'en sers..

    Tu as souvent suffisamment de headers à gérer avec les différents modules pour ne pas en plus te traîner à recopier à chaque fois tout ça...

    Enfin, c'est mon expérience....

  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 souviron34 Voir le message
    euh... tu cites C++ et Windows...
    Disons que la problématique des entêtes est un point commun avec les deux langages (pour une fois...). Tu peux remplacer mon std::string par un time_t si tu préfères et obtenir le même cas d'utilisation.
    Après, Windows est aussi un cas fréquent pour le dév, ça reste une part majoritaire des PC existants. C'est moins sensible sur les entêtes du kernel Linux, mais ça reste vrai sur les entêtes de librairies un peu plus complexes et/ou graphiques qui trainent en général des "monstres" derrière le premier include, ou encore la plupart des frameworks portables tels que ACE.

    Citation Envoyé par souviron34 Voir le message
    En général, par exemple sur un projet, tu fais un "CommonC.h", qui va inclure les headers généraux dont on a toujours besoin (stdio, stdlib, time, limits, ...), et tu t'en sers..
    Et tu rends ainsi tous tes modules dépendants d'un autre, ou trainant des dépendances inutiles pour des modules qui pourraient ne dépendre QUE d'entêtes standards.
    Tu le sais très certainement aussi bien que moi, cet entête "commun" va vite hériter de définitions propres au projet, ou d'un lien vers un module quelconque, ou d'une dépendance à une librairie. C'est comme ça, en général, que l'on finit par avoir une IHM graphique qui te réclame un entête de kernel... Une aberration, à mon sens.

    Cerise sur le gâteau, dans la plupart des cas, cet entête commun n'est même pas précompilé... Ou alors, au contraire, on a l'effet inverse (courant hélas sous Windows via stdafx.h, l'entête précompilé créé automatiquement) : TOUTES les dépendances sont incluses dedans, et chaque fichier C/CPP n'inclus QUE l'entête précompilé. Le jour où tu dois démêler les spaghettis, ou lorsque tu rajoutes un #include, tu pleures...

    Citation Envoyé par souviron34 Voir le message
    Tu as souvent suffisamment de headers à gérer avec les différents modules pour ne pas en plus te traîner à recopier à chaque fois tout ça...
    Si effectivement tu le fais en copier/coller, on est d'accord : c'est mal. Notamment parce que tu as la plupart du temps une dépendance inutile qui est rajoutée.
    Pour ma part, je ne recopie pas : comme je l'ai dit, je fais des entêtes complets. J'ajoute au cas par cas les #include dans le .C / .CPP en fonction de mes besoins, et si ça demande l'inclusion d'une nouvelle librairie, je me pose sérieusement la question "en ai-je réellement besoin ?", ce qui me permet de soit me rendre compte que je vais "trop loin" dans ma fonctionnalité, soit que j'ai oublié une séparation de code qui devrait être présente.

    Mon but premier, c'est d'avoir les modules les plus indépendants possibles, connectés entre eux par des interfaces "abstraites" et génériques. La raison est de pouvoir tester unitairement les modules au maximum via un stub, et d'obtenir une portabilité maximale via les interfaces.

    Certes, c'est une problématique propre à mon métier et à mon expérience. Mais pour avoir souvent dû remettre en ordre du code fait par des windowsiens / linuxiens "spécialistes", je peux te garantir que tu apprends assez vite à avoir une démarche un peu différente de la démarche "habituelle". Et je ne te parles même pas du nombre de fois où j'ai pu déplacer des modules d'une librairie à l'autre pour de simples raisons de dépendances crétines (ou pire : circulaires !!) faites par des inclusions sauvages et/ou via un "common.h"... Quand tu n'arrives pas à la situation délirante suivante :
    - Ça ne compile pas !
    - Normal, tu n'as pas inclus "bouse.h".
    - Mais si, je l'ai inclus pourtant.
    - Ah oui, mais tu l'as inclus après "immonde.h", il faut l'inclure avant.
    - Mais pourquoi ?
    - On ne sait pas, celui qui l'a fait est parti il y a deux ans, et personne n'ose y toucher.

    Bien entendu, c'est du vécu, tu t'en doutes. En l'occurrence, on trainait une librairie infecte importée en partie par l'un des headers, en partie par l'autre, et à un moment donné il y avait une redéfinition non protégée (et le warning était planqué via un #pragma)...

    Il est toujours bon de savoir que ce que l'on considère comme "bon", "souhaitable" ou "pratique" peut être une vraie plaie ouverte dans d'autres situations, et surtout pourquoi.

  10. #10
    Expert éminent sénior

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 66
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Points : 17 923
    Points
    17 923
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Mac LAK Voir le message
    Certes, c'est une problématique propre à mon métier et à mon expérience
    Je pense, oui, car :



    Citation Envoyé par Mac LAK Voir le message
    Tu le sais très certainement aussi bien que moi, cet entête "commun" va vite hériter de définitions propres au projet, ou d'un lien vers un module quelconque, ou d'une dépendance à une librairie. C'est comme ça, en général, que l'on finit par avoir une IHM graphique qui te réclame un entête de kernel... Une aberration, à mon sens.
    je n'ai jamais vu ceci... JAMAIS....


    Et c'est même le contraire.....


    Les entêtes se font par foncionalités, par modules...

    Il est certain que si on se met à faire "classes" en tête, on va droit dans le mur..

    Mais la plupart des codes sur lesquels j'ai travaillé étaient par décuopuages fonctionnels, et donc en général aucun problème..

    Un "CommonC" ne sera jamais touché une fois le démarrage du projet effectué.

    Une IHM aura un "MainWin.h", un "PrintWin.h", etc

    Il y aura un "MATHS.h", un "Images.h", etc etc....

    Chacun de ces derniers incluera CommonC, c'est sûr puqique le projet est en C.

    Mais pour le reste, tout sera inclus en fonction du besoin uniquement...





    Franchement, je crois (alors je ne sais pas si c'est le monde Win ou la proximité de l'enseignement OO qui veut ça) que c'est ton milieu/expérience qui crée ce genre de choses....

  11. #11
    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 souviron34 Voir le message
    je n'ai jamais vu ceci... JAMAIS....
    Ben tu as bien de la chance, ou alors, tu avais peu de personnes bossant sur le projet... Avec une valse fréquente d'assistants et/ou de pseudo-dévs qui tripotent suivant les trous de planning, cela arrive constamment pourtant... Et j'ai constaté ça aussi dans d'autres domaines que le mien (lorsqu'il y a valse d'intervenants), c'est même le sujet préféré d'anecdotes avec mes amis qui bossent également dans l'informatique (le fameux "Tu sais pas la dernière qu'ils m'ont pondu, ces enclumes ?" "Racontes !").

    Citation Envoyé par souviron34 Voir le message
    Mais la plupart des codes sur lesquels j'ai travaillé étaient par décuopuages fonctionnels, et donc en général aucun problème..
    Chez nous aussi. C'est justement là le problème, aussi : va trouver un point commun entre un système d'acquisition temps réel sur des cartes électroniques, et un routeur TCP/IP aux communications bas niveau et à l'envoi de données vers les IHM... Les deux sont des threads du même processus, bien entendu. Rajoutes à ça le fait d'être multi plate-forme (tout en restant optimisé et temps réel, bien entendu), et tu vas commencer à mieux cerner le problème.

    Au final, t'as TOUJOURS quelqu'un qui ajoute un truc quelque part qui "pollue" les autres composants, si l'on part sur le principe de faire des entêtes de facilité.

    Citation Envoyé par souviron34 Voir le message
    Mais pour le reste, tout sera inclus en fonction du besoin uniquement...
    Tout à fait d'accord : on inclut suivant le BESOIN. Je ne vois pas l'intérêt d'inclure "math.h" si l'on ne fait pas d'opérations mathématiques dans un module, pour ma part... Simplement, moi, je le fais dès le début.

    Citation Envoyé par souviron34 Voir le message
    Franchement, je crois (alors je ne sais pas si c'est le monde Win ou la proximité de l'enseignement OO qui veut ça) que c'est ton milieu/expérience qui crée ce genre de choses....
    Ou plus simplement de travailler sur des applications qui fonctionnent, sont améliorées et maintenues pendant 10 à 25 ans... Avec ce que cela implique comme turn-over et évolutions technologiques pendant cette période.

    Après, comme je l'ai déjà dit plusieurs fois, le milieu industriel / embarqué est un milieu caché : cela ne veut pas dire qu'il est inexistant ou négligeable en terme de postes pourvus, bien au contraire, et le problème que j'expose est malheureusement quasi-inévitable sur un projet devant être maintenu aussi longtemps.

Discussions similaires

  1. Solution pour les include d'include ?
    Par Nowwis dans le forum Langage
    Réponses: 1
    Dernier message: 01/08/2010, 22h01
  2. Réponses: 8
    Dernier message: 04/04/2009, 12h14
  3. Bouml Preserved Body pour les includes
    Par escafr dans le forum BOUML
    Réponses: 11
    Dernier message: 15/04/2008, 10h47
  4. Réponses: 6
    Dernier message: 28/09/2004, 16h47
  5. Règles pour les #include
    Par julian_ross dans le forum MFC
    Réponses: 2
    Dernier message: 24/02/2004, 09h57

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