Question 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 et ensuite file.cpp peut référencer file.h avec un #include "file.h" directif.

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

J'ai quelques questions sur tout cela:

  1. Quels sont les avantages de cette organisation de structure de fichiers?
  2. Pourquoi sont .h fichiers à l'intérieur include/ inclus avec #include "file.h" au lieu de #include "include/file.h"? Je sais que le vrai truc se trouve dans un fichier Makefile, mais est-ce vraiment mieux de faire cela au lieu de préciser (en code) que le fichier que nous voulons inclure est en fait dans le include/ annuaire?

23
2018-02-01 03:13


origine


Réponses:


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

Ainsi, lorsque vous utilisez la bibliothèque, vous créez un lien vers le binaire et ajoutez les fichiers de la bibliothèque. include répertoire vers les 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 déterminer quels fichiers doivent être copiés dans l’emplacement central (les fichiers binaires compilés et les fichiers binaires). include répertoire) et quels fichiers ne le font pas (le répertoire source, etc.).


27
2018-02-01 03:18



C'était autrefois <header> Le style inclut le type de chemin implicite, c’est-à-dire que includes chemin de variable d'environnement ou une macro de construction, et le "header" style inclut de manière explicite, comme dans, exactement par rapport à l’endroit où se trouve le fichier source. Bien que certaines chaînes d’outils de compilation permettent toujours cette distinction, elles utilisent souvent une configuration qui l’annule effectivement.

Votre question est intéressante car elle soulève la question de savoir quelle est vraiment meilleure, implicite ou explicite? La forme implicite est certainement plus facile car:

  1. Regroupements pratiques d'en-têtes liés dans les hiérarchies de répertoires.
  2. Vous avez seulement besoin d'inclure quelques répertoires dans le includes chemin et n'ont pas besoin d'être au courant de 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 d'autre part a:

  1. Répétable construit. Une réorganisation des chemins dans un includes La variable macro / environnement 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 développeur.
  3. Proximité de l'information. Vous savez exactement où se trouve l'en-tête #include "\X\Y\Z". Dans la forme implicite, vous devrez peut-être 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 depuis plusieurs décennies au sujet de ces deux approches, mais une forme hybride des deux, principalement gagnante grâce à l’effort requis pour maintenir des builds purement explicites, et la difficulté évidente de se familiariser avec le code. de nature purement implicite. Nous comprenons tous en général que nos différentes chaînes d’outils placent certaines bibliothèques et en-têtes communs dans des emplacements particuliers, de sorte qu’ils peuvent être partagés entre les utilisateurs et les projets. Nous prévoyons donc de trouver des en-têtes C / C ++ standard Ne rien savoir de la structure spécifique de tout projet arbitraire, sans convention bien documentée localement. Nous nous attendons donc à ce que le code dans ces projets soit explicite en ce qui concerne les bits non standard qui leur sont uniques et implicites en ce qui concerne les bits standard.

Il est recommandé de toujours utiliser le <header> forme d'inclure pour tous les en-têtes standard et autres bibliothèques qui ne sont pas spécifiques au projet et d'utiliser le "header" forme pour tout le reste. Si vous avez un répertoire include dans votre projet pour vos inclusions locales? Cela dépend dans une certaine mesure si ces en-têtes seront livrés en tant qu'interfaces vers vos bibliothèques ou simplement consommés par votre code, ainsi que par vos préférences. Quelle est la taille et la complexité de votre projet? Si vous disposez d'un mélange d'interfaces internes et externes ou de nombreux composants différents, vous pouvez regrouper les éléments dans des répertoires distincts.

Gardez à l'esprit que le répertoire de structure de votre produit fini se décompose, n'a pas besoin de ressembler à la structure de répertoire dans laquelle vous développez et générez ce produit. Si vous ne disposez que de quelques fichiers et en-têtes .c / .cpp, 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 d’environnement de construction et, si tout va bien, le documenter pour que les autres le comprennent.


6
2018-02-01 04:17



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

Utilities.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 .cpp pour chaque plate-forme, et les .cpp peuvent être placés à des endroits différents pour être facilement sélectionnés 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".


2
2018-02-01 07:42