web-dev-qa-db-fra.com

Quels sont les avantages d'un chemin relatif tel que "../include/header.h" pour un en-tête?

J'ai passé en revue les questions Comment utiliser correctement la directive include et C++ #include semantics et ne répond pas à cela - ni les autres suggérées par SO = quand j'ai tapé le titre ...

Quels sont, le cas échéant, les avantages de l'écriture:

#include "../include/someheader.h"
#include "../otherdir/another.h"

par rapport à l'utilisation d'un simple nom de fichier:

#include "someheader.h"
#include "another.h"

ou peut-être un nom relatif sans le '.. ':

#include "include/someheader.h"
#include "otherdir/another.h"

Les problèmes que je vois sont:

  • Vous ne pouvez pas déplacer un en-tête sans vous soucier des fichiers source qui l'incluent.
  • Vous pouvez vous retrouver avec des chemins très longs pour les en-têtes dans les dépendances et les rapports d'erreur. J'en ai eu un aujourd'hui avec "../dir1/include/../../include/../dir2/../include/header.h ".

Le seul mérite que je peux voir est que, même si vous n'avez pas besoin de déplacer des fichiers, vous pouvez peut-être vous en sortir sans toujours utiliser '-I 'pour trouver des en-têtes, mais la perte de flexibilité et la complexité de la compilation dans des sous-sous-répertoires, etc. semblent l'emporter sur l'avantage.

Alors, est-ce que j'oublie un avantage?


Merci pour les contributions. Je pense que le consensus est qu'il n'y a pas d'avantages majeurs à la notation en utilisant ".." que j'écarte. En termes généraux, j'aime la notation "quelque part/en-tête.h"; Je l'utilise dans de nouveaux projets. Celui sur lequel je travaille est tout sauf nouveau.

L'un des problèmes est qu'il existe différents ensembles d'en-têtes, souvent avec un préfixe tel que rspqr.h, rsabc.h, rsdef.h, rsxyz.h. Ceux-ci sont tous liés au code dans le répertoire rsmp, mais certains en-têtes sont dans rsmp et d'autres sont dans le répertoire central include, qui n'a pas de sous-répertoires tels que rsmp dedans. (Et répétez pour les divers autres domaines du code; il y a des en-têtes à plusieurs endroits, nécessaires au hasard par d'autres bits de code.) Le déplacement de choses est un problème majeur parce que le code est devenu si compliqué au fil des ans. Et les makefiles ne sont pas cohérents dans lesquels -I les options sont fournies. Dans l'ensemble, c'est une triste histoire de négligence pas si bénigne sur une période de décennies. Réparer le tout sans casser quoi que ce soit va être un travail long et fastidieux.

53
Jonathan Leffler

Je préfère la syntaxe du chemin car elle rend très clair à quel espace de noms ou module appartient le fichier d'en-tête.

#include "Physics/Solver.h"

Est très auto-descriptif sans nécessiter que chaque module préfixe son nom aux fichiers d'en-tête.

Cependant, je n'utilise presque jamais la syntaxe "..", mais à la place, mon projet inclut les emplacements de base corrects.

38
Andrew Grant

Le problème avec #include "../include/header.h" est que cela fonctionnera souvent par accident, puis un changement apparemment sans rapport le fera cesser de fonctionner plus tard.

Par exemple, considérez la disposition source suivante:

./include/header.h
./lib/library.c
./lib/feature/feature.c

Et disons que vous exécutez le compilateur avec un chemin d'inclusion de -I. -I./lib. Ce qui se produit?

  • ./lib/library.c peut faire #include "../include/header.h", ce qui est logique.
  • ./lib/feature/feature.c peut aussi faire #include "../include/header.h", même si cela n'a pas de sens. En effet, le compilateur essaiera le #include directive relative à l'emplacement du fichier actuel, et si cela échoue, il essaiera la #include directive relative à chaque -I entrée dans le #include chemin.

De plus, si vous supprimez ultérieurement -I./lib du #include chemin, puis vous cassez ./lib/feature/feature.c.

Je trouve que quelque chose comme ce qui suit est préférable:

./projectname/include/header.h
./projectname/lib/library.c
./projectname/lib/feature/feature.c

Je n'ajouterais aucune entrée de chemin d'inclusion autre que -I., puis les deux library.c et feature.c voudrais utiliser #include "projectname/include/header.h". En supposant que le "nom du projet" est susceptible d'être unique, cela ne devrait pas entraîner de collisions de noms ou d'ambiguïtés dans la plupart des circonstances. Vous pouvez également utiliser le chemin d'inclusion et/ou la fonction VPATH de make pour diviser la disposition physique du projet sur plusieurs répertoires si cela est absolument nécessaire (pour accueillir du code généré automatiquement spécifique à la plate-forme, par exemple; c'est quelque chose qui casse vraiment vers le bas lorsque vous utilisez #include "../../somefile.h").

24
bk1e

IANALL, mais je ne pense pas que vous devriez mettre .. se trouve dans les fichiers source C ou C++ réels, car ce n'est pas portable et la norme ne le prend pas en charge. Cela revient à utiliser \ est sous Windows. Ne le faites que si votre compilateur ne peut pas fonctionner avec une autre méthode.

9

Commencez le chemin de vos directives #include "" Par une séquence d'un ou plusieurs "../" Lorsque:

  • vous souhaitez inclure un fichier dont la collocation avec le fichier inclus est fixe et
  • vous construisez sur un système POSIX ou avec VC++ et
  • vous souhaitez éviter toute ambiguïté sur le fichier qui sera inclus.

Il est toujours facile de fournir un exemple où votre base de code contient une erreur et où cela provoque par la suite un échec difficile à diagnostiquer. Cependant, même si votre projet est sans défaut, il peut être utilisé abusivement par un tiers si vous comptez sur des chemins absolus pour spécifier les fichiers situés les uns par rapport aux autres.

Par exemple, considérez la disposition de projet suivante:

./your_lib/include/foo/header1.h
./your_lib/include/bar/header2.h
./their_lib/include/bar/header2.h

Comment your_lib/include/foo/header1.h devrait inclure your_lib/include/bar/header2.h ? Considérons deux options:

  1. #include <bar/header2.h>

    En supposant que your_lib/include et their_lib/include sont cités comme recherche d'en-tête chemins (par exemple en utilisant les options -I ou -isystem de GCC), puis le choix de header2.h sera choisi est sensible à l'ordre dans lequel ces deux chemins sont recherchés.

  2. #include "../bar/header2.h"

    Le premier emplacement dans lequel le compilateur recherchera est l'emplacement de your_lib/include/foo/header1.h , qui est your_lib/include/foo/. Il essaiera d'abord your_lib/include/foo /../ bar/header2.h qui se réduit à your_lib/inclure/bar/header2.h où il trouvera le bon fichier. Les chemins de recherche d'en-tête ne seront pas utilisés du tout et il y a peu de place pour l'ambiguïté.

Je recommanderais fortement l'option 2) dans ce cas pour les raisons données.

En réponse à certains arguments dans d'autres réponses:

  • @ andrew-grant dit :

    ... il indique très clairement à quel espace de noms ou module appartient le fichier d'en-tête.

    Peut être. Mais les chemins relatifs peuvent être interprétés comme "dans ce même module". Ils clarifient le cas où plusieurs répertoires portant le même nom se trouvent dans différents modules.

  • @ bk1e dit :

    ... cela fonctionnera souvent par accident ...

    Je dirais que les chemins relatifs ne fonctionneront que par accident dans de très rares cas où le projet a été interrompu dès le départ et pourrait facilement être corrigé. Une telle collision de noms sans provoquer d'erreur de compilation semble peu probable. Un scénario courant est celui où le fichier d'un projet dépendant inclut l'un de vos en-têtes qui en inclut un autre. La compilation de votre suite de tests devrait entraîner une erreur "Aucun fichier ou répertoire de ce type" lors de la compilation indépendamment de ce projet dépendant.

  • @singlenegationelimination dit

    ... ce n'est pas portable et la norme ne le prend pas en charge.

    La norme ISO C peut ne pas spécifier tous les détails des systèmes sous lesquels un programme est compilé ou exécuté. Cela ne signifie pas qu'ils ne sont pas pris en charge, mais simplement que la norme ne spécifie pas trop les plates-formes sur lesquelles C s'exécutera. (La distinction entre la façon dont "" Et <> Sont interprétés sur les systèmes modernes courants provient probablement dans la norme POSIX.)

  • @ daniel-paull dit

    le ".." suppose un emplacement relatif et est fragile

    Fragile comment? Vraisemblablement sensible à la collocation des deux fichiers. Ainsi, ".." Ne doit (et toujours) être utilisé que lorsque l'auteur du fichier inclus contrôle son emplacement.

4
John McFarlane

Un autre problème sur les fenêtres avec des chemins relatifs est MAX_PATH. Cela déclenchera des problèmes de compilation lorsque, par ex. compilation croisée pour Android et votre chemin dépasse la longueur de 260.

2
MadSystem

Considérez votre arborescence source comme un espace de noms imbriqué et le chemin d'inclusion vous permet d'extraire des répertoires à la racine de cet espace de noms. La question est alors de former un espace de noms logique pour votre base de code indépendamment de la façon dont le code est organisé sur le disque.

J'éviterais des chemins comme:

  • "include/foo/bar.h" - le "include" semble illogique et redondant
  • "../foo/bar.h" - le ".." suppose un emplacement relatif et est fragile
  • "bar.h" - sauf si bar.h est dans le répertoire courant, cela pollue l'espace de noms global et demande des ambiguïtés.

Personnellement, j'ai tendance à ajouter un chemin comme le suivant à mes projets: chemin - "..;../..;../../..;../../../..".

Cela vous permet d'appliquer une sorte de règle de masquage à votre #includes et permet une certaine liberté de déplacement des en-têtes sans casser un autre code. Bien sûr, cela se fait au détriment de l'introduction d'un risque de liaison au mauvais fichier d'en-tête si vous ne faites pas attention car les noms non pleinement qualifiés peuvent être (ou devenir au fil du temps) ambigus.

J'ai tendance à me qualifier entièrement #includes dans les en-têtes publics afin que les tiers consommant mon code n'aient pas besoin d'ajouter le "..;../..;../../..;../../../.." à leur projet - c'est juste une commodité pour mon code privé et mon système de construction.

2
Daniel Paull

Parce qu'alors vous placez le fichier par rapport à la racine du projet, et lorsque vous l'archivez dans le contrôle de code source et qu'un autre développeur l'extrait à un emplacement différent sur leur système local, les choses fonctionnent toujours.

1
Joel Coehoorn