Le préprocesseur C peut-il être utilisé pour savoir si un fichier existe?
-
02-07-2019 - |
Question
J'ai une très grande base de code (lire: des milliers de modules) qui partage le code entre de nombreux projets s'exécutant sur différents systèmes d'exploitation avec différents compilateurs C ++. Inutile de dire que maintenir le processus de construction peut être une corvée.
Il y a plusieurs endroits dans la base de code où il faudrait nettoyer le code de manière substantielle si seulement il existait un moyen de faire en sorte que le pré-processeur ignore certains #includes
si le fichier n'existait pas dans le fichier. dossier en cours. Quelqu'un sait-il 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 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é-génération pour maintenir 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érerais de beaucoup le faire de cette façon. Des idées?
La solution
Généralement, vous utilisez un script qui tente d’exécuter le préprocesseur lors d’une tentative d’inclusion du fichier. Selon que le préprocesseur renvoie une erreur ou non, le script met à jour le fichier .h généré avec un #define (ou #undef) approprié. Dans Bash, le script pourrait ressembler vaguement à ceci:
cat > .test.h <<'EOM'
#include <asdf.h>
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
Autres conseils
Petite mise à jour
Certains compilateurs peuvent prendre en charge __ has_include (nom-en-tête)
.
L'extension a été ajoutée à la norme C ++ 17 ( P0061R1 ).
Support du compilateur
- Clang
- GCC from 5.X
- Visual Studio à partir de VS2015 Update 2 (?)
Exemple (site Web clang):
// Note the two possible file name string formats.
#if __has_include("myinclude.h") && __has_include(<stdint.h>)
# include "myinclude.h"
#endif
Sources
Créez un dossier spécial pour les en-têtes manquants et effectuez la dernière recherche dans ce dossier
(spécifique au complémenteur - dernier élément de la variable d'environnement "INCLUDES", quelque chose comme cela)
Ensuite, si un en-tête1.h peut être manquant, créez un fichier de remplacement dans ce dossier
header1.h:
#define header1_is_missing
Maintenant, vous pouvez toujours écrire
#include <header1.h>
#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 construction pour le faire. Je connais surtout make, qui vous permettrait de faire quelque chose comme ceci dans votre makefile:
ifdef $(test -f filename && echo "present")
DEFINE=-DFILENAME_PRESENT
endif
Bien sûr, vous devriez trouver un analogue à celui-ci dans d'autres environnements de construction tels que VisualStudio, mais je suis sûr qu'ils existent.
Vous pourriez avoir une étape préalable à la construction qui génère un fichier d'inclusion contenant une liste de #defines représentant les noms des fichiers existant dans le répertoire en cours:
#define EXISTS_FILE1_C
#define EXISTS_FILE1_H
#define EXISTS_FILE2_C
Ensuite, incluez ce fichier dans votre code source afin que votre source puisse tester les définitions EXISTS _ *
afin de déterminer 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 accomplir cela avec un peu d'aide du Makefile, si vous utilisez la même marque sur toutes les plateformes. 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 l’a mentionné, vous pouvez alors conditionner vos #includes à des conditions:
#ifdef HEADER1_INC
#include <header1.h>
#endif
Autre possibilité: remplir un répertoire quelque part avec des versions nuls de tous les en-têtes que vous souhaitez éventuellement inclure. Passez un argument -I dans ce répertoire en tant que dernière de cette option.
GCC cpp recherche ses répertoires d’inclusion dans l’ordre, s’il trouve un fichier d’en-tête dans un répertoire antérieur, il l’utilisera. Sinon, il trouvera éventuellement le fichier de longueur nulle et sera heureux.
Je suppose que d'autres implémentations de cpp recherchent également leurs répertoires d'inclusion dans l'ordre spécifié.
Je devais 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 vous souhaitez inclure des en-têtes ou des liens vers des 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 surprend pour beaucoup de 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
puis 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
---------- termine check.mk
incluez le fichier check.mk dans votre fichier bld.inf, il DOIT l'être avant vos fichiers MMP
PRJ_MMPFILES
gnumakefile checkmedialist.mk
maintenant à la compilation, le fichier file_strange_supported.h
aura un drapeau approprié.
vous pouvez utiliser cet indicateur dans vos fichiers cpp ou même dans le fichier mmp
par exemple en mmp
#include "../inc/file_strange_supported.h"
#ifdef NEW_API_SUPPORTED
LIBRARY newapi.lib
#else
LIBRARY oldapi.lib
#endif
et au format .cpp
#include "../inc/file_strange_supported.h"
#ifdef NEW_API_SUPPORTED
CStrangeApi* api = Api::NewLC();
#else
// ..
#endif
Contrairement à certaines affirmations présentées ici et sur Internet, Visual Studio 2015 NE prend PAS en charge la fonctionnalité __ has_include
- du moins selon mon expérience. Testé avec la mise à jour 3.
Les rumeurs pourraient provenir du fait que VS 2017 est également appelée "version 15"; VS 2015 est désigné à la place par "Version 14". La prise en charge de cette fonctionnalité semble avoir été officiellement introduite avec "Visual Studio 2017 version 15.3".