web-dev-qa-db-fra.com

Pourquoi créer un répertoire include / dans les projets C et C ++?

Lorsque je travaille sur mes projets personnels C et C++, je mets généralement file.h Et file.cpp Dans le même répertoire, puis file.cpp Peut faire référence à file.h Avec un #include "file.h" Directive.

Cependant, il est courant de trouver des bibliothèques et d'autres types de projets (comme le noyau Linux et freeRTOS) où tous les fichiers .h Sont placés dans un répertoire include/, Tandis que .cpp les fichiers restent dans un autre répertoire. Dans ces projets, les fichiers .h Sont également inclus avec #include "file.h" Au lieu de #include "include/file.h", Comme je l'espérais.

J'ai quelques questions à propos de tout ça:

  1. Quels sont les avantages de cette organisation de structure de fichiers?
  2. Pourquoi les fichiers .h Dans include/ Sont-ils inclus avec #include "file.h" Au lieu de #include "include/file.h"? Je sais que le vrai truc se trouve dans certains fichiers Makefile, mais est-il préférable de le faire plutôt que de préciser (en code) que le fichier que nous souhaitons inclure est en réalité dans le répertoire include/?
27
user4546925

La raison principale en est que les bibliothèques compilées ont besoin d’en-têtes pour être consommées par l’utilisateur final. Par convention, le contenu du répertoire include correspond aux en-têtes exposés à la consommation publique. Le répertoire source peut avoir des en-têtes à usage interne, mais ceux-ci sont non destinés à être distribués avec la bibliothèque compilée.

Ainsi, lorsque vous utilisez la bibliothèque, vous vous connectez au binaire et ajoutez le répertoire include de la bibliothèque aux chemins d’en-tête de votre système de construction. De même, si vous installez votre bibliothèque compilée dans un emplacement centralisé, vous pouvez indiquer quels fichiers doivent être copiés vers cet emplacement (les fichiers binaires compilés et le répertoire include) et les autres (le répertoire source). et ainsi de suite).

31
Nicol Bolas

C'était autrefois <header> style include étaient du type chemin implicite, c’est-à-dire se trouvant sur le chemin de la variable d’environnement includes ou une macro de construction, et le "header" style include était de la forme explicite, en tant que-dedans, exactement par rapport à l'endroit où le fichier source est celui qui l'a inclus. Bien que certaines chaînes d’outils de construction autorisent encore cette distinction, elles utilisent souvent par défaut une configuration qui l’annule.

Votre question est intéressante parce qu’elle soulève la question de ce qui est vraiment meilleur, implicite ou explicite? La forme implicite est certainement plus facile car:

  1. Regroupement pratique des en-têtes associés dans les hiérarchies de répertoires.
  2. Il vous suffit d'inclure quelques répertoires dans le chemin includes et vous n'avez pas besoin de connaître tous les détails en ce qui concerne l'emplacement exact des fichiers. Vous pouvez modifier les versions des bibliothèques et leurs en-têtes associés sans modifier le code.
  3. SEC .
  4. Flexible! Votre environnement de construction ne doit pas nécessairement correspondre au mien, mais nous pouvons souvent obtenir des résultats presque identiques.

Explicite a d'autre part:

  1. Constructions répétables. Une réorganisation des chemins dans une variable macro/environnement includes ne modifie pas les fichiers d'en-tête résultants trouvés lors de la construction.
  2. Constructions portables. Il suffit de tout emballer à partir de la racine de la construction et de l’envoyer à un autre dev.
  3. Proximité de l'information. Vous savez exactement où se trouve l'en-tête avec #include "\X\Y\Z". Sous la forme implicite, il se peut que vous deviez rechercher plusieurs chemins et même trouver plusieurs versions du même fichier. Comment savoir lequel est utilisé dans la construction?

Les constructeurs se disputent ces deux approches depuis de nombreuses décennies, mais une forme hybride des deux l'emporte en grande partie à cause de l'effort nécessaire pour maintenir des constructions basées uniquement sur la forme explicite, et de la difficulté évidente à se familiariser avec le code. de nature purement implicite. Nous comprenons tous généralement que nos différentes chaînes d’outils placent certaines bibliothèques et certains en-têtes communs dans des emplacements particuliers, de sorte qu’ils puissent être partagés par les utilisateurs et les projets; ne connaissons rien de la structure spécifique de tout projet arbitraire, en l'absence d'une convention localement bien documentée, nous nous attendons donc à ce que le code de ces projets soit explicite en ce qui concerne les bits non standard qui leur sont propres et implicite en ce qui concerne les bits standard.

C'est une bonne pratique de toujours utiliser le <header> forme d’inclusion pour tous les en-têtes standard et autres bibliothèques non spécifiques à un projet et pour utiliser le "header" forme pour tout le reste. Devez-vous avoir un répertoire include dans votre projet pour vos inclus locaux? Cela dépend dans une certaine mesure si ces en-têtes seront livrés en tant qu'interfaces avec vos bibliothèques ou simplement utilisés par votre code, ainsi que de vos préférences. Quelle est la taille et la complexité de votre projet? Si vous avez une combinaison d'interfaces internes et externes ou de nombreux composants différents, vous pouvez regrouper les éléments dans des répertoires distincts.

N'oubliez pas que la structure de répertoires dans laquelle votre produit fini décompresse ne doit pas ressembler à la structure de répertoires sous laquelle vous développez et construisez ce produit. Si vous ne disposez que de quelques fichiers .c/.cpp et en-têtes, vous pouvez les placer tous dans un seul répertoire, mais vous finirez par travailler sur quelque chose de non trivial et devrez réfléchir aux conséquences de vos choix en matière d’environnement de construction et, espérons-le, le documenter pour que les autres le comprennent.

7
jwdonahue

1 . .hpp et .cpp ne doivent pas nécessairement avoir une relation 1 pour 1, il peut y avoir plusieurs .cpp utilisant le même fichier .hpp selon différentes conditions (par exemple: environnements différents), par exemple: une bibliothèque multi-plateforme, imaginons qu'il existe une classe pour obtenir la version de l'application, et l'en-tête est comme ça:

Utilitaires.h

#include <string.h>
class Utilities{
    static std::string getAppVersion();
}

main.cpp

#include Utilities.h
int main(){
    std::cout << Utilities::getAppVersion() << std::ends;
    return 0;
}

il peut y avoir un fichier .cpp pour chaque plate-forme, et le fichier .cpp peut être placé à différents endroits de manière à pouvoir être facilement sélectionné par la plate-forme correspondante, par exemple:

.cpp pour iOS (chemin: DemoProject/ios/Utilities.cpp):

#include "Utilities.h"
std::string Utilities::getAppVersion(){
    //some Objective C code
}

.cpp pour Android (chemin: DemoProject/Android/Utilities.cpp)):

#include "Utilities.h"
std::string Utilities::getAppVersion(){
    //some jni code
}

et bien sûr, 2 .cpp ne seraient pas utilisés en même temps normalement.


2.

#include "file.h" 

au lieu de

#include "include/file.h" 

vous permet de garder le code source inchangé lorsque vos en-têtes ne sont plus placés dans le dossier "include".

1
ocomfd