Quel ordre faut-il spécifier pour inclure les fichiers, c’est-à-dire quelles sont les raisons pour 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 d'inclusion locaux?
Je ne pense pas qu'il y ait un ordre recommandé, tant qu'il compile! Ce qui est embêtant, c’est que certains en-têtes nécessitent d’inclure d’abord les en-têtes… C’est un problème avec les en-têtes eux-mêmes, et non avec l’ordre des inclus.
Ma préférence personnelle est d’aller du local au global, chaque sous-titre par ordre alphabétique, c’est-à-dire:
Mon raisonnement pour 1. est qu'il doit prouver que chaque en-tête (pour lequel il y a un cpp) peut être #include
d sans conditions préalables. Et le reste semble découler logiquement de là.
Il est important de garder à l'esprit que vos en-têtes ne doivent pas dépendre d'autres en-têtes. Une façon de s’assurer que c’est d’inclure vos en-têtes avant tout autre en-tête.
"Thinking in C++" en particulier le mentionne, en faisant référence à la "conception de logiciel C++ à grande échelle" de Lakos:
Les erreurs d'utilisation latentes peuvent être évitées en veillant à ce que le fichier .h d'un composant soit analysé lui-même - sans déclarations ni définitions fournies en externe ... L'inclusion du fichier .h dans la toute première ligne du fichier .c garantit qu'aucune pièce essentielle ne soit critique. Il manque des informations intrinsèques à l'interface physique du composant dans le fichier .h (ou, le cas échéant, vous le saurez dès que vous essayez de compiler le fichier .c).
C'est-à-dire, incluez dans l'ordre suivant:
Si l'un des en-têtes a un problème avec l'inclusion dans cette commande, corrigez-le (si le vôtre) ou ne l'utilisez pas. Boycott des bibliothèques qui n'écrivent pas des en-têtes propres.
Guide de style C++ de Google argumente presque l'inverse, sans aucune justification du tout; Personnellement, j'ai tendance à privilégier l'approche des Lakos.
Je suis deux règles simples qui évitent la grande majorité des problèmes:
Je suis aussi les directives de:
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. Les règles d’autre part, j’applique de manière rigide, au point même de fournir des fichiers d’entêtes 'wrapper' avec des gardes include et des include groupées si un développeur tiers odieux ne souscrit pas à ma vision :-)
Pour ajouter ma propre brique au mur.
Donc je vais généralement comme ça:
// 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 de la suivante:
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 noms, simplement parce qu'il est plus facile de les rechercher de cette façon.
Je suis presque sûr que ce n'est pas une pratique recommandée dans le monde sain d'esprit, mais j'aime bien 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 par ordre d'inclusion.
Je recommande:
Et bien sûr, ordre alphabétique dans chaque section, si possible.
Utilisez toujours des déclarations anticipées pour éviter les #include
s inutiles dans vos fichiers d’en-tête.
Ce n'est pas subjectif. Assurez-vous que vos en-têtes ne reposent pas sur le fait d'être #include
d dans un ordre spécifique. Vous pouvez être sûr que l'ordre dans lequel vous incluez les en-têtes STL ou Boost importe peu.
Commencez par inclure l'en-tête correspondant au fichier .cpp ... en d'autres termes, source1.cpp
doit inclure source1.h
avant d'inclure quoi que ce soit d'autre. 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.h
avant toute autre chose.
Reasoning: Inclure le source1.h
avant tout autre fichier garantit qu'il peut être autonome sans ses dépendances. Si source1.h
prend une dépendance à une date ultérieure, le compilateur vous avertira immédiatement d'ajouter les déclarations de transfert requises à source1.h
. Cela garantit 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
...
tilisateurs de 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 changeront jamais) vers stdafx.h
.
Incluez du plus spécifique au moins spécifique, en commençant par le .hpp correspondant au .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.
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'inclusion d'en-tête précompilé, sans que votre projet ne soit spécifique au compilateur.
C’est une question difficile dans le monde du C/C++, avec tant d’éléments dépassant la norme.
Je pense que l'ordre des fichiers d'en-tête n'est pas un problème grave tant qu'il compile, comme dit squelart.
Mon idée est la suivante: 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 résolu ultérieurement en ajoutant des lignes #include au fichier défectueux .h.
Le véritable problème survient lorsque certains en-têtes modifient leur action (en vérifiant #if conditions) 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 offsetof
dans leurs en-têtes système, j’écrirai dans mon entête:
#ifndef offsetof
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#endif
Et assurez-vous de dire à l'utilisateur de #include "custom.h"
after tous les en-têtes système, sinon la ligne de offsetof
dans stddef.h affirmera une erreur de redéfinition des macros.
Nous prions pour que de tels cas ne se rencontrent plus dans notre carrière.