C / C ++ inclut l'ordre des fichiers d'en-tête

287

Quel ordre doit inclure les fichiers à spécifier, c'est-à-dire quelles sont les raisons d'inclure un en-tête avant un autre?

Par exemple, les fichiers système, STL et Boost vont-ils avant ou après les fichiers include locaux?

Anycorn
la source
2
Et la pléthore de réponses ci-dessous explique pourquoi les développeurs Java ont décidé de ne pas en-tête séparé. :-) Quelques très bonnes réponses, cependant, en particulier l'avertissement pour vous assurer que vos propres fichiers d'en-tête peuvent être autonomes.
Chris K
37
J'aime la façon dont les questions qui ont plus de 100 votes et qui sont manifestement intéressantes pour beaucoup de gens sont considérées comme "non constructives".
Andreas
Une lecture fortement recommandée: cplusplus.com/forum/articles/10627
Kalsan
3
@mrt, SO rappelle fortement la communauté nazie de la soupe: Soit vous suivez des règles très strictes, soit "Pas de bonne réponse / commentaires pour vous!". Néanmoins, si quelqu'un a un problème lié à la programmation, c'est (généralement) le premier site à aller ..
Imago

Réponses:

289

Je ne pense pas qu'il y ait un ordre recommandé, tant qu'il compile! Ce qui est ennuyeux, c'est lorsque certains en-têtes exigent que d'autres en-têtes soient inclus en premier ... C'est un problème avec les en-têtes eux-mêmes, pas avec l'ordre des inclusions.

Ma préférence personnelle est de passer du local au global, chaque sous-section par ordre alphabétique, c'est-à-dire:

  1. fichier h correspondant à ce fichier cpp (le cas échéant)
  2. en-têtes du même composant,
  3. en-têtes d'autres composants,
  4. en-têtes du système.

Mon raisonnement pour 1. est qu'il devrait prouver que chaque en-tête (pour lequel il y a un cpp) peut être #included sans prérequis (terminus technicus: l'en-tête est "autonome"). Et le reste semble découler logiquement de là.

squelart
la source
16
À peu près la même chose que vous, sauf que je passe du global au local et que l'en-tête correspondant au fichier source ne reçoit pas de traitement spécial.
Jon Purdy
127
@ Jon: Je dirais que c'est à peu près le contraire! :-) Je dirais que votre méthode peut introduire des dépendances cachées, disons que si myclass.cpp inclut <string> puis <myclass.h>, il n'y a aucun moyen de comprendre au moment de la construction que myclass.h peut lui-même dépendre de string; donc si plus tard, vous ou quelqu'un d'autre, incluez myclass.h mais n'avez pas besoin de chaîne, vous obtiendrez une erreur qui doit être corrigée dans le cpp ou dans l'en-tête lui-même. Mais je serais intéressé de savoir si c'est ce que les gens pensent qui fonctionnerait mieux à long terme ... Pourquoi ne postez-vous pas de réponse avec votre proposition et nous verrons qui "gagne"? ;-)
squelart
3
Le spécifique à la commande générale est ce que j'utilise actuellement à partir de la recommandation de Dave Abrahams. Et il note la même raison que @squelart d'illuminer l'en-tête manquant dans les sources, du local au plus général. La clé importante étant que vous êtes plus susceptible de commettre ces erreurs que les bibliothèques tierces et système.
GrafikRobot
7
@PaulJansen C'est une mauvaise pratique et il est bon d'utiliser une technique qui est plus susceptible de exploser avec elle afin que la mauvaise pratique puisse être corrigée au lieu de rester cachée. FTW local à mondial
bames53
10
@PaulJansen Oui, je faisais référence à un comportement standard de contournement. Cela pourrait se produire par accident, tout comme, par exemple, la rupture du RLL peut se produire par accident. La solution n'est pas d'utiliser des pratiques qui se cachent lorsque de tels accidents se produisent, mais d'utiliser des pratiques qui sont le plus susceptibles de les faire exploser le plus fort possible, afin que les erreurs puissent être constatées et corrigées le plus tôt possible.
bames53
106

La grande chose à garder à l'esprit est que vos en-têtes ne doivent pas dépendre des autres en-têtes inclus en premier. Une façon de garantir cela est d'inclure vos en-têtes avant tout autre en-tête.

"Penser en C ++" le mentionne en particulier, en faisant référence au "Large Software C ++ Software Design" de Lakos:

Les erreurs d'utilisation latentes peuvent être évitées en s'assurant que le fichier .h d'un composant est analysé par lui-même - sans déclarations ni définitions fournies en externe ... L'inclusion du fichier .h en tant que toute première ligne du fichier .c garantit qu'aucune pièce critique des informations intrinsèques à l'interface physique du composant sont manquantes dans le fichier .h (ou, le cas échéant, vous en serez informé dès que vous tenterez de compiler le fichier .c).

C'est-à-dire, inclure dans l'ordre suivant:

  1. En-tête prototype / interface pour cette implémentation (c'est-à-dire le fichier .h / .hh qui correspond à ce fichier .cpp / .cc).
  2. Autres en-têtes du même projet, au besoin.
  3. En-têtes d'autres bibliothèques non standard et non système (par exemple, Qt, Eigen, etc.).
  4. En-têtes d'autres bibliothèques "presque standard" (par exemple, Boost)
  5. En-têtes C ++ standard (par exemple, iostream, fonctionnel, etc.)
  6. En-têtes C standard (par exemple, cstdint, dirent.h, etc.)

Si l'un des en-têtes a un problème avec l'inclusion dans cette commande, corrigez-les (si le vôtre) ou ne les utilisez pas. Libérez les bibliothèques qui n'écrivent pas d'en-têtes propres.

Le guide de style C ++ de Google soutient presque l'inverse, sans vraiment aucune justification; Personnellement, j'ai tendance à privilégier l'approche Lakos.

Nathan Paul Simons
la source
13
À partir de maintenant, Google C ++ Style Guide recommande d'inclure d'abord le fichier d'en-tête associé, suivant la suggestion de Lakos.
Filip Bártek
Vous n'allez pas beaucoup plus loin que le premier en-tête associé, car une fois que vous aurez commencé à inclure vos en-têtes dans le projet, vous allez tirer un grand nombre de dépendances système.
Micah
@Micah - les en-têtes dans le projet qui tirent "beaucoup de dépendances du système" sont une mauvaise conception, précisément ce que nous essayons d'éviter ici. Le but est d'éviter à la fois les inclusions inutiles et les dépendances non résolues. Tous les en-têtes doivent pouvoir être inclus sans inclure d'autres en-têtes au préalable. Dans le cas où un en-tête dans le projet a besoin d'une dépendance système, qu'il en soit ainsi - alors vous n'incluez pas (et ne devriez pas) la dépendance système après, sauf si le code local de ce fichier utilise des éléments de ce système. Vous ne pouvez pas et ne devez pas vous baser sur les en-têtes (même les vôtres) pour inclure les dépôts système que vous utilisez.
Nathan Paul Simons
49

Je respecte deux règles simples qui évitent la grande majorité des problèmes:

  1. Tous les en-têtes (et en fait tous les fichiers source) doivent inclure ce dont ils ont besoin. Ils ne doivent pas compter sur leurs utilisateurs, y compris sur les choses.
  2. En complément, tous les en-têtes devraient avoir des gardes afin qu'ils ne soient pas inclus plusieurs fois par une application trop ambitieuse de la règle 1 ci-dessus.

Je respecte également les directives de:

  1. Incluez d'abord les en-têtes du système (stdio.h, etc.) avec une ligne de séparation.
  2. Groupez-les logiquement.

En d'autres termes:

#include <stdio.h>
#include <string.h>

#include "btree.h"
#include "collect_hash.h"
#include "collect_arraylist.h"
#include "globals.h"

Bien que, étant des lignes directrices, c'est une chose subjective. D'un autre côté, j'applique les règles de manière rigide, même au point de fournir des fichiers d'en-tête 'wrapper' avec des gardes d'inclusion et des inclusions groupées si un développeur tiers désagréable ne souscrit pas à ma vision :-)

paxdiablo
la source
6
+1 "Tous les en-têtes (et en fait tous les fichiers source) doivent inclure ce dont ils ont besoin. Ils ne doivent pas compter sur leurs utilisateurs, y compris sur les choses." Pourtant, beaucoup de gens s'appuient sur ce comportement d'inclusion implicite, par exemple, avec NULL et n'incluent pas <cstddef>. C'est tellement ennuyeux lorsque vous essayez de porter ce code et d'obtenir des erreurs de compilation sur NULL (une raison pour laquelle j'utilise simplement 0 maintenant).
stinky472
20
Pourquoi incluez-vous d'abord les en-têtes du système? Il vaudrait mieux l'autre pourquoi à cause de votre première règle.
jhasse
Si vous utilisez des macros de test de fonctionnalités, votre première inclusion ne devrait probablement PAS être un en-tête de bibliothèque standard. Par conséquent, pour la généralité, je dirais que la politique "locale d'abord, globale plus tard" est la meilleure.
hmijail pleure les démissionnaires
1
Concernant votre première suggestion de "ne pas compter sur les utilisateurs", qu'en est-il des déclarations de transfert dans le fichier d'en-tête qui n'ont pas besoin que le fichier d'en-tête soit inclus? Devrions-nous toujours inclure le fichier d'en-tête parce que les déclarations avancées imposent à l'utilisateur du fichier d'en-tête d'inclure les fichiers appropriés.
Zoso
22

Pour ajouter ma propre brique au mur.

  1. Chaque en-tête doit être autosuffisant, ce qui ne peut être testé que s'il est inclus en premier au moins une fois
  2. Il ne faut pas modifier par erreur la signification d'un en-tête tiers en introduisant des symboles (macro, types, etc.)

Donc je vais généralement comme ceci:

// myproject/src/example.cpp
#include "myproject/example.h"

#include <algorithm>
#include <set>
#include <vector>

#include <3rdparty/foo.h>
#include <3rdparty/bar.h>

#include "myproject/another.h"
#include "myproject/specific/bla.h"

#include "detail/impl.h"

Chaque groupe séparé par une ligne vierge du suivant:

  • En-tête correspondant à ce fichier cpp en premier (contrôle de cohérence)
  • En-têtes système
  • En-têtes tiers, organisés par ordre de dépendance
  • En-têtes de projet
  • En-têtes privés du projet

Notez également que, à part les en-têtes système, chaque fichier se trouve dans un dossier avec le nom de son espace de noms, simplement parce qu'il est plus facile de les retrouver de cette façon.

Matthieu M.
la source
2
Pour que les autres fichiers d'en-tête ne soient pas affectés par eux. À la fois par ce que ces en-têtes système définissent (les inclusions X et les inclusions Windows sont mauvaises à propos #definede ceux qui gâchent d'autres codes) et pour empêcher les dépendances implicites. Par exemple, si notre fichier d'en-tête de base de code foo.hdépend vraiment <map>mais que partout où il a été utilisé dans des .ccfichiers, <map>était déjà inclus, nous ne le remarquerions probablement pas. Jusqu'à ce que quelqu'un essaie d'inclure foo.hsans d'abord inclure <map>. Et puis ils seraient ennuyés.
@ 0A0D: Le deuxième problème n'est pas un problème dans l'ordre ici, car chacun en .ha au moins un .cppqui l'inclut en premier (en effet, dans mon code personnel, le test unitaire associé l'inclut d'abord, et le code source l'inclut dans son groupe légitime ). En ce qui concerne le fait de ne pas être influencé, si l'un des en-têtes inclut, <map>alors tous les en-têtes inclus par la suite sont influencés de toute façon, donc cela me semble une bataille perdue.
Matthieu M.
1
Bien sûr, c'est pourquoi je fais régulièrement le tour et corrige le code plus ancien (ou même le code plus récent) qui nécessite une inclusion inutile car cela augmente simplement les temps de construction.
@MatthieuM. J'aimerais connaître la raison d'être de votre premier point, c.-à-d Header corresponding to this cpp file first (sanity check). Y a-t-il quelque chose de particulier s'il #include "myproject/example.h"est déplacé à la fin de tous les inclusions?
MNS
1
@MNS: un en-tête doit être autonome, c'est-à-dire qu'il ne devrait pas être nécessaire d'inclure un autre en-tête avant. Il est de votre responsabilité, en tant que rédacteur de l'en-tête, de vous en assurer, et la meilleure façon de le faire est d'avoir un fichier source dans lequel cet en-tête est inclus en premier. L'utilisation du fichier source correspondant au fichier d'en-tête est facile, un autre bon choix est d'utiliser le fichier source de test unitaire correspondant au fichier d'en-tête mais il est moins universel (il peut ne pas y avoir de tests unitaires).
Matthieu M.
16

Je recommande:

  1. L'en-tête du module .cc que vous créez. (Aide à garantir que chaque en-tête de votre projet n'a pas de dépendances implicites avec les autres en-têtes de votre projet.)
  2. Fichiers système C.
  3. Fichiers système C ++.
  4. Plate-forme / OS / autres fichiers d'en-tête (par exemple win32, gtk, openGL).
  5. Autres fichiers d'en-tête de votre projet.

Et bien sûr, l'ordre alphabétique dans chaque section, si possible.

Utilisez toujours des déclarations avancées pour éviter les #includes inutiles dans vos fichiers d'en-tête.

i_am_jorf
la source
+1, mais pourquoi alphabétique? On dirait que quelque chose peut vous faire vous sentir mieux, mais n'a aucun avantage pratique.
Ben
9
L'ordre alphabétique est un ordre arbitraire, mais facile. Vous n'avez pas à faire alphabétique, mais vous devez choisir un ordre afin que tout le monde le fasse de manière cohérente. J'ai trouvé que cela aide à éviter les doublons et facilite les fusions. Et si vous utilisez du texte sublime, F5 les commandera pour vous.
i_am_jorf
14

Je suis presque sûr que ce n'est pas une pratique recommandée partout dans le monde sain d'esprit, mais j'aime aligner le système en fonction de la longueur du nom de fichier, trié lexicalement dans la même longueur. Ainsi:

#include <set>
#include <vector>
#include <algorithm>
#include <functional>

Je pense que c'est une bonne idée d'inclure vos propres en-têtes avant les autres peuples, pour éviter la honte de la dépendance des ordres d'inclusion.

clstrfsck
la source
3
J'aime trier mes en-têtes en utilisant une clé composée de la deuxième, de la troisième puis de la première lettre dans cet ordre :-) Donc, vecteur, ensemble, algorithme, fonctionnel pour votre exemple.
paxdiablo
@paxdiablo, merci pour l'astuce. J'envisage de l'utiliser, mais je crains que cela finisse par laisser la pile de noms de fichiers instable et susceptible de basculer. Qui sait ce qui pourrait être inclus si cela se produit - peut-être même windows.h.
clstrfsck
40
Tri par longueur ? Folie!
James McNellis
1
+1 pour le premier. Cela a du sens en fait, si vous avez besoin de localiser visuellement les en-têtes dans un fichier avec vos yeux, c'est beaucoup mieux que par ordre alphabétique.
Kugel
6

Ce n'est pas subjectif. Assurez-vous que vos en-têtes ne dépendent pas d'être #included dans un ordre spécifique. Vous pouvez être sûr que peu importe l'ordre dans lequel vous incluez les en-têtes STL ou Boost.

wilhelmtell
la source
1
Je ne supposais aucune dépendance implicite
Anycorn
Oui, mais le compilateur ne peut pas faire cette hypothèse, donc #include <A>, <B> n'est jamais le même que #include <B>, <A> tant qu'ils n'ont pas été compilés.
Mikhail
4

Commencez par inclure l'en-tête correspondant au .cpp ... en d'autres termes, source1.cppdevrait inclure source1.havant d'inclure autre chose. La seule exception à laquelle je peux penser est lorsque vous utilisez MSVC avec des en-têtes précompilés, auquel cas, vous êtes obligé d'inclure stdafx.havant toute autre chose.

Raisonnement: l' inclusion de source1.htout autre fichier avant garantit qu'il peut être autonome sans ses dépendances. Si source1.hprend une dépendance à une date ultérieure, le compilateur vous alertera immédiatement pour ajouter les déclarations avancées requises à source1.h. Cela garantit à son tour que les en-têtes peuvent être inclus dans n'importe quel ordre par leurs personnes à charge.

Exemple:

source1.h

class Class1 {
    Class2 c2;    // a dependency which has not been forward declared
};

source1.cpp

#include "source1.h"    // now compiler will alert you saying that Class2 is undefined
                    // so you can forward declare Class2 within source1.h
...

Utilisateurs MSVC: je recommande fortement d'utiliser des en-têtes précompilés. Donc, déplacez toutes les #includedirectives pour les en-têtes standard (et les autres en-têtes qui ne changeront jamais) vers stdafx.h.

Agnel Kurian
la source
2

Inclure du plus spécifique au moins spécifique, en commençant par le .hpp correspondant pour le .cpp, s'il en existe un. De cette façon, toutes les dépendances cachées dans les fichiers d'en-tête qui ne sont pas autosuffisantes seront révélées.

Cela est compliqué par l'utilisation d'en-têtes précompilés. Une façon de contourner cela, sans rendre votre compilateur de projet spécifique, consiste à utiliser l'un des en-têtes de projet comme fichier d'inclusion d'en-tête précompilé.

dcw
la source
1

C'est une question difficile dans le monde C / C ++, avec autant d'éléments au-delà de la norme.

Je pense que l'ordre des fichiers d'en-tête n'est pas un problème sérieux tant qu'il se compile, comme l'a dit squelart.

Mes idées sont les suivantes: s'il n'y a pas de conflit de symboles dans tous ces en-têtes, tout ordre est OK et le problème de dépendance d'en-tête peut être résolu plus tard en ajoutant des lignes #include au .h défectueux.

Le vrai problème survient lorsqu'un en-tête modifie son action (en vérifiant les conditions #if) en fonction des en-têtes ci-dessus.

Par exemple, dans stddef.h dans VS2005, il y a:

#ifdef  _WIN64
#define offsetof(s,m)   (size_t)( (ptrdiff_t)&(((s *)0)->m) )
#else
#define offsetof(s,m)   (size_t)&(((s *)0)->m)
#endif

Maintenant, le problème: si j'ai un en-tête personnalisé ("custom.h") qui doit être utilisé avec de nombreux compilateurs, y compris certains anciens qui ne fournissent pas offsetofdans leurs en-têtes système, je devrais écrire dans mon en-tête:

#ifndef offsetof
#define offsetof(s,m)   (size_t)&(((s *)0)->m)
#endif

Et assurez-vous d'indiquer à l'utilisateur #include "custom.h" après tous les en-têtes du système, sinon la ligne de offsetofstddef.h affirmera une erreur de redéfinition de macro.

Nous prions de ne plus rencontrer de tels cas dans notre carrière.

Jimm Chen
la source