En lisant les détails du commutateur -I
Dans GCC, je suis plutôt choqué de constater que l’utiliser sur la ligne de commande annule les inclusions système. À partir du documents du préprocesseur
"Vous pouvez utiliser
-I
Pour remplacer un fichier d'en-tête système, en substituant votre propre version, car ces répertoires sont recherchés avant les répertoires de fichiers d'en-tête système standard."
Ils ne semblent pas mentir. Sur deux systèmes Ubuntu différents avec GCC 7, si je crée un fichier endian.h
:
#error "This endian.h shouldn't be included"
... et ensuite dans le même répertoire, créez un main.cpp
(ou main.c, même différence):
#include <stdlib.h>
int main() {}
Puis, compiler avec g++ main.cpp -I. -o main
(Ou avec une différence identique) me donne:
In file included from /usr/include/x86_64-linux-gnu/sys/types.h:194:0,
from /usr/include/stdlib.h:394,
from /usr/include/c++/7/cstdlib:75,
from /usr/include/c++/7/stdlib.h:36,
from main.cpp:1:
./endian.h:1:2: error: #error "This endian.h shouldn't be included"
Donc stdlib.h inclut ce fichier types.h, qui à la ligne 194 dit simplement #include <endian.h>
. Mon apparente idée fausse (et peut-être celle des autres) était que les crochets auraient empêché cela, mais -Je suis plus fort que je ne le pensais.
Bien que pas fort assez, parce que vous ne pouvez même pas le réparer en collant/usr/include dans la première ligne de commande, parce que:
"Si un répertoire include système standard, ou un répertoire spécifié avec
-isystem
, Est également spécifié avec-I
, L'option-I
Est ignorée. Le répertoire est toujours recherché mais en tant que répertoire système à sa position normale dans la chaîne d'inclusion système. "
En effet, la sortie prolixe de g++ -v main.cpp -I/usr/include -I. -o main
Laisse/usr/include au bas de la liste:
#include "..." search starts here:
#include <...> search starts here:
.
/usr/include/c++/7
/usr/include/x86_64-linux-gnu/c++/7
/usr/include/c++/7/backward
/usr/lib/gcc/x86_64-linux-gnu/7/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/7/include-fixed
/usr/include/x86_64-linux-gnu
/usr/include
Colore moi surpris. Je suppose que pour en faire une question:
Pour quelle raison légitime la plupart des projets utilisent-ils -I
Compte tenu de ce problème extrêmement grave? Vous pouvez remplacer les en-têtes arbitraires sur les systèmes en fonction nom des collisions. Tout le monde ne devrait-il pas utiliser plutôt -iquote
?
Quelles sont les raisons légitimes pour -I
Sur -iquote
? -I
Est normalisé (au moins par POSIX ) alors que -iquote
Ne l'est pas. (En pratique, j'utilise -I
Car minuscule (l'un des compilateurs avec lesquels je souhaite compiler mon projet) ne prend pas en charge -iquote
.)
Comment les projets gèrent-ils avec -I
Étant donné les dangers? Vous incluez les inclus dans un répertoire et utilisez -I pour ajouter le répertoire contenant ce répertoire.
includes/mylib/endian.h
-Iincludes
#include "mylib/endian.h" //or <mylib/endian.h>
Avec cela, tant que vous n'entrez pas en conflit sur le nom mylib
, vous n'entrez pas en conflit (du moins en ce qui concerne les noms d'en-tête).
En regardant les manuels de GCC, on dirait que -iquote
et d’autres options n’ont été ajoutées que dans GCC 4: https://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/Directory-Options.html#Directory%20Options
Donc, l'utilisation de "-I"
est probablement une combinaison de: habitude, paresse, compatibilité avec les versions antérieures, ignorance des nouvelles options, compatibilité avec d’autres compilateurs.
La solution consiste à "l'espace de noms" de vos fichiers d'en-tête en les plaçant dans des sous-répertoires. Par exemple, placez votre en-tête Endian dans "include/mylib/endian.h"
puis ajouter "-Iinclude"
en ligne de commande et vous pouvez #include "mylib/endian.h"
qui ne devrait pas entrer en conflit avec d'autres bibliothèques ou bibliothèques système.
Je cela votre prémisse que c'est -I
c'est dangereux, c'est faux. La langue laisse la recherche pour les fichiers d’en-tête avec la forme #include
suffisamment défini par l'implémentation pour qu'il soit dangereux d'utiliser des fichiers d'en-tête en conflit avec les noms des fichiers d'en-tête standard. Abstenez-vous simplement de le faire.
Un cas évident est la compilation croisée. GCC souffre un peu de l'hypothèse UNIX historique selon laquelle vous compilez toujours pour votre système local, ou du moins quelque chose de très proche. C'est pourquoi les fichiers d'en-tête du compilateur se trouvent à la racine du système. L'interface épurée est manquante.
En comparaison, Windows ne suppose aucun compilateur et les compilateurs Windows ne supposent pas que vous ciblez le système local. C'est pourquoi vous pouvez avoir un ensemble de compilateurs et un ensemble de SDK installés.
Maintenant en compilation croisée, GCC se comporte beaucoup plus comme un compilateur pour Windows. Il ne suppose plus que vous avez l'intention d'utiliser les en-têtes du système local, mais vous permet de spécifier exactement les en-têtes souhaités. Et évidemment, il en va de même pour les bibliothèques avec lesquelles vous créez un lien.
Notez maintenant que lorsque vous faites cela, l’ensemble des en-têtes de remplacement est conçu pour continuer top du système de base. Vous pouvez laisser des en-têtes dans le jeu de remplacement si leur implémentation est identique. Par exemple. les chances sont que <complex.h>
est le même. Il n'y a pas beaucoup de variation dans les implémentations de nombres complexes. Cependant, vous ne pouvez pas remplacer de manière aléatoire des bits d’implémentation internes tels que <endian.h>
.
TL, DR: cette option s’adresse aux personnes qui savent ce qu’elles font. "Etre dangereux" n'est pas un argument pour le public cible.