C / C ++ inclut l’ordre des fichiers / les meilleures pratiques

Y a-t-il un ordre recommandé dans lequel les fichiers d’inclusion doivent être spécifiés?

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

Y a-t-il une raison particulière de choisir l’une ou l’autre? Je suppose que les fichiers d’inclusion ont des dépendances appropriées spécifiées.

Je ne pense pas qu’il y ait un ordre recommandé, tant qu’il comstack! Ce qui est ennuyeux, c’est que certains en-têtes nécessitent l’inclusion d’autres en-têtes … 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. h fichier correspondant à ce fichier cpp (le cas échéant)
  2. les en-têtes du même composant,
  3. en-têtes d’autres composants,
  4. en-têtes du système.

Ma raison d’être 1. est qu’il devrait prouver que chaque en-tête (pour lequel il y a un cpp) peut être #include d sans conditions préalables. Et le rest semble juste couler logiquement à partir de là.

La grande chose à garder à l’esprit est que vos en-têtes ne doivent pas dépendre de l’inclusion d’autres en-têtes. Une façon de s’assurer de cela est d’inclure vos en-têtes avant tout autre en-tête.

“Réflexion en C ++” en fait mention, faisant référence à la “conception de logiciel C ++ à grande échelle” de Lakos:

Les erreurs d’utilisation latente peuvent être évitées en veillant à ce que le fichier .h d’un composant soit analysé par lui-même – sans déclarations ou définitions fournies en externe … L’inclusion du fichier .h comme première ligne du fichier .c garantit qu’aucun élément critique des informations insortingnsèques à l’interface physique du composant sont absentes du fichier .h (ou, le cas échéant, que vous en saurez dès que vous essayez de comstackr le fichier .c).

C’est-à-dire inclure dans l’ordre suivant:

  1. L’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 non-système (par exemple, Qt, Eigen, etc.).
  4. En-têtes d’autres bibliothèques “quasi 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 son inclusion dans cette commande, corrigez-le (si le vôtre) ou ne l’utilisez pas. Boycott des bibliothèques qui n’écrivent pas les en-têtes propres.

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

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

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

Je suis également les directives de:

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

En d’autres termes:

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

Bien que, étant des lignes direcsortingces, c’est une chose subjective. Les règles, par contre, que j’applique de manière rigide, au sharepoint fournir des fichiers d’en-tête «wrapper» avec des gardes inclus et groupés, incluent si un développeur tiers odieux ne souscrit pas à ma vision 🙂

Ajouter ma propre brique au mur.

  1. Chaque en-tête doit être autonome, ce qui ne peut être testé que s’il est inclus 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 ça:

 // myproject/src/example.cpp #include "myproject/example.h" #include  #include  #include  #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 vide du suivant:

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

Notez également que, mis à part les en-têtes système, chaque fichier se trouve dans un dossier portant le nom de son espace de nom, simplement parce qu’il est plus facile de les suivre de cette manière.

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

 #include  #include  #include  #include  

Je pense que c’est une bonne idée d’inclure vos propres en-têtes avant les autres peuples, afin d’éviter la honte de la dépendance à l’ordre d’inclusion.

Je recommande:

  1. L’en-tête du module .cc que vous créez. (Permet de s’assurer que chaque en-tête de votre projet n’a pas de dépendances implicites sur les autres en-têtes de votre projet.)
  2. C fichiers système.
  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 directes pour éviter les #include inutiles dans vos fichiers d’en-tête.

Ce n’est pas subjectif. Assurez-vous que vos en-têtes ne reposent pas sur #include d 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.

D’abord, incluez l’en-tête correspondant au fichier .cpp … En d’autres termes, source1.cpp doit inclure source1.h avant d’inclure autre chose. La seule exception à laquelle je puisse penser est l’utilisation de MSVC avec des en-têtes pré-compilés, auquel cas, vous êtes obligé d’inclure stdafx.h avant toute autre chose.

Raisonnement: l’ inclusion de source1.h avant tout autre fichier garantit qu’il peut restr autonome sans ses dépendances. Si source1.h prend une dépendance à une date ultérieure, le compilateur vous alertera immédiatement pour append les déclarations de source1.h requirejses à source1.h . Cela garantit que les personnes à charge peuvent inclure les en-têtes dans n’importe quel ordre.

Exemple:

source1.h

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

source1.cpp

 #include "source1.h" // now comstackr 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 directives #include pour les en-têtes standard (et les autres en-têtes qui ne vont jamais changer) vers stdafx.h .

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 auto-suffisantes seront révélées.

Ceci est compliqué par l’utilisation d’en-têtes pré-compilés. Une solution consiste à utiliser l’un des en-têtes de projet comme fichier d’en-tête précompilé sans que votre projet soit spécifique au compilateur.

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 comstack, 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 correct et le problème de dépendance d’en-tête peut être corrigé plus tard en ajoutant des lignes #include aux .h défectueux.

Le problème est réel quand un en-tête change 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 de offsetof dans 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 de dire à l’utilisateur d’ #include "custom.h" après tous les en-têtes du système, sinon, la ligne de offsetof dans stddef.h affirmera une erreur de redéfinition de la macro.

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