Le préprocesseur C peut-il être utilisé pour savoir si un fichier existe?

J’ai une base de code très volumineuse (lisez: des milliers de modules) dont le code est partagé entre de nombreux projets, tous exécutés sur des systèmes d’exploitation différents avec des compilateurs C ++ différents. Inutile de dire que le maintien du processus de construction peut être une corvée.

Il y a plusieurs endroits dans la base de code où le code serait nettoyé de façon substantielle si seulement il y avait un moyen de faire en sorte que le pré-processeur ignore certains #includes si le fichier n’existait pas dans le dossier actuel. Est-ce que quelqu’un sait comment y parvenir?

Actuellement, nous utilisons un #ifdef autour du #include dans le fichier partagé, avec un deuxième fichier spécifique au projet qui # définit si oui ou non le #include existe dans le projet. Cela fonctionne, mais c’est moche. Les gens oublient souvent de mettre à jour correctement les définitions lorsqu’ils ajoutent ou suppriment des fichiers du projet. J’ai envisagé d’écrire un outil de pré-compilation pour garder ce fichier à jour, mais s’il existe un moyen indépendant de la plate-forme de le faire avec le préprocesseur, je préfère plutôt le faire à la place. Des idées?

En général, cela se fait à l’aide d’un script qui tente d’exécuter le préprocesseur lors d’une tentative d’inclusion du fichier. Selon si le préprocesseur renvoie une erreur, le script met à jour un fichier .h généré avec un #define (ou #undef) approprié. En bash, le script pourrait ressembler vaguement à ceci:

 cat > .test.h <<'EOM' #include  EOM if gcc -E .test.h then echo '#define HAVE_ASDF_H 1' >> config.h else echo '#ifdef HAVE_ASDF_H' >> config.h echo '# undef HAVE_ASDF_H' >> config.h echo '#endif' >> config.h fi 

Autoconf est un framework assez complet pour travailler de manière portable avec des contrôles de portabilité comme celui-ci (ainsi que des milliers d’autres).

Petite mise à jour

Certains compilateurs peuvent prendre en charge __has_include ( header-name ) .

L’extension a été ajoutée à la norme C ++ 17 ( P0061R1 ).

Support du compilateur

  • Bruit
  • GCC à partir de 5.X
  • Visual Studio à partir de VS2015 Update 2 (?)

Exemple (du site clang):

 // Note the two possible file name ssortingng formats. #if __has_include("myinclude.h") && __has_include() # include "myinclude.h" #endif 

Sources

  • SD-6: Recommandations de test de fonctionnalité SG10
  • Extensions de la langue de clang

Créez un dossier spécial pour les en-têtes manquants et effectuez une recherche dans ce dossier en dernier
(qui est spécifique au complément – dernier élément de la variable d’environnement “INCLUDES”, quelque chose comme ça)

Alors si certains header1.h peuvent être manquants, créez dans ce dossier un stub

header1.h:

 #define header1_is_missing 

Maintenant, vous pouvez toujours écrire

 #include  #ifdef header1_is_missing // there is no header1.h #endif 

Le préprocesseur lui-même ne peut pas identifier l’existence de fichiers, mais vous pouvez certainement utiliser l’environnement de génération pour le faire. Je suis surtout familier avec make, ce qui vous permettrait de faire quelque chose comme ça dans votre makefile:

 ifdef $(test -f filename && echo "present") DEFINE=-DFILENAME_PRESENT endif 

Bien sûr, il faudrait trouver un analogue dans d’autres environnements de construction comme VisualStudio, mais je suis sûr qu’ils existent.

Vous pouvez avoir une étape de pré-génération qui génère un fichier d’inclusion contenant une liste de #defines représentant les noms des fichiers existant dans le répertoire actuel:

 #define EXISTS_FILE1_C #define EXISTS_FILE1_H #define EXISTS_FILE2_C 

Ensuite, incluez ce fichier dans votre code source, puis votre source peut tester la EXISTS_* pour voir si un fichier existe ou non.

Pour autant que je sache, cpp n’a pas de directive concernant l’existence d’un fichier.

Vous pourrez peut-être y parvenir avec un peu d’aide du Makefile, si vous utilisez la même marque sur toutes les plates-formes. Vous pouvez détecter la présence d’un fichier dans le Makefile:

 foo.o: foo.c if [ -f header1.h ]; then CFLAGS+=-DHEADER1_INC 

Comme @Greg Hewgill le mentionne, vous pouvez alors faire en sorte que vos #includes soient conditionnels:

 #ifdef HEADER1_INC #include  #endif 

Autre possibilité: remplir un répertoire avec des versions de longueur nulle de tous les en-têtes à inclure facultativement. Transmettez un argument -I à ce répertoire comme dernière option.

GCC cpp recherche ses répertoires include dans l’ordre, s’il trouve un fichier d’en-tête dans un répertoire antérieur, il l’utilisera. Sinon, il finira par trouver le fichier de longueur zéro et soyez heureux.

Je suppose que les autres implémentations de cpp recherchent également leurs répertoires d’inclusion dans l’ordre spécifié.

J’ai dû faire quelque chose de similaire pour le système d’exploitation Symbian. Voici comment je l’ai fait: disons que vous voulez vérifier si le fichier “file_strange.h” existe et que vous souhaitez inclure des en-têtes ou des liens vers certaines bibliothèques en fonction de l’existence de ce fichier.

Créez d’abord un petit fichier de commandes pour vérifier l’existence de ce fichier.

autoconf est bon, mais il est inutile pour de nombreux petits projets.

———- check.bat

 @echo off IF EXIST [\epoc32\include\domain\middleware\file_strange] GOTO NEW_API GOTO OLD_API GOTO :EOF :NEW_API echo.#define NEW_API_SUPPORTED>../inc/file_strange_supported.h GOTO :EOF :OLD_API echo.#define OLD_API_SUPPORTED>../inc/file_strange_supported.h GOTO :EOF 

———- check.bat se termine

alors j’ai créé un fichier gnumake

———-checkmedialist.mk

 do_nothing : @rem do_nothing MAKMAKE : check.bat BLD : do_nothing CLEAN : do_nothing LIB : do_nothing CLEANLIB : do_nothing RESOURCE : do_nothing FREEZE : do_nothing SAVESPACE : do_nothing RELEASABLES : do_nothing FINAL : do_nothing 

———-check.mk se termine

inclure le fichier check.mk dans votre fichier bld.inf, il DOIT être avant vos fichiers MMP

 PRJ_MMPFILES gnumakefile checkmedialist.mk 

maintenant, au moment de la compilation, le fichier file_strange_supported.h aura un jeu de drapeaux approprié. vous pouvez utiliser cet indicateur dans vos fichiers cpp ou même dans le fichier mmp par exemple dans mmp

 #include "../inc/file_strange_supported.h" #ifdef NEW_API_SUPPORTED LIBRARY newapi.lib #else LIBRARY oldapi.lib #endif 

et dans .cpp

 #include "../inc/file_strange_supported.h" #ifdef NEW_API_SUPPORTED CStrangeApi* api = Api::NewLC(); #else // .. #endif