Can I use the C preprocessor to determine if a file exists?

I have a very large code base (read: thousands of modules), which has a common code for many projects that all work on different operating systems with different C ++ compilers. Of course, maintaining the assembly process can be quite challenging.

There are several places in the code base where it could substantially clear the code if there was a way to force the preprocessor to ignore a specific #includes if the file did not exist in the current folder. Does anyone know a way to achieve this?

We are currently using #ifdef around #include in a shared file with a second project-specific file that # determines whether or not #include exists in the project. It works, but it is ugly. People often forget to update definitions correctly when adding or removing files from a project. I was considering writing a pre-build tool to keep this file up to date, but if it were a platform-independent way of doing this using a preprocessor, I would rather do it that way. Any ideas?

+46
c ++ include c-preprocessor
Sep 27 '08 at 3:22
source share
8 answers

Typically, this is done using a script that tries to start the preprocessor when trying to include a file. Depending on whether the preprocessor returns an error, the script updates the generated .h file with the corresponding #define (or #undef). In bash, the script may not look like this:

 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 

A rather complicated basis for portable work with portability checks such as this (as well as thousands of others), autoconf .

+30
Sep 27 '08 at 3:47 april
source share

Create a special folder for missing headers and make this folder search last
(that is, specific to the compiler - the last element in the environment variable "INCLUDES", something like this)

Then, if some header1.h may be missing, create a stub in this folder

header1.h:

 #define header1_is_missing 

Now you can always write

 #include <header1.h> #ifdef header1_is_missing // there is no header1.h #endif 
+39
Sep 27 '08 at 4:34
source share

Little update

Some compilers may support __has_include ( header-name ) .

The extension was added in C ++ 17 standard ( P0061R1 ).

Compiler support

  • Clang
  • GCC from 5.X
  • Visual Studio from VS2015 Update 2 (?)

Example (from clang website):

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

Sources

+25
Oct 21 '15 at 13:03
source share

The preprocessor itself cannot identify the existence of files, but of course you can use the build environment to do this. I am mostly familiar with make, which will allow you to do something like this in your makefile:

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

Of course, you will have to find an analogue of this in other build environments such as VisualStudio, but I'm sure they exist.

+6
Sep 27 '08 at 3:48
source share

You can have a pre-build step that generates an include file containing a list of #defines that represent the names of the files existing in the current directory:

 #define EXISTS_FILE1_C #define EXISTS_FILE1_H #define EXISTS_FILE2_C 

Then include this file from the source code, and then your source can check EXISTS_* to determine if the file exists.

+4
Sep 27 '08 at 3:27
source share

As far as I know, cpp has no directive regarding the existence of a file.

You may be able to accomplish this with a little help from the Makefile if you use the same make on different platforms. You can detect the presence of a file in the Makefile:

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

As @Greg Hewgill mentions, you can make your #includes conditional:

 #ifdef HEADER1_INC #include <header1.h> #endif 
+4
Sep 27 '08 at 3:54 on
source share

Another possibility is to populate the directory somewhere with zero versions of all the headers you want to include. Pass the -I argument to this directory as the last of such an option.

GCP cpp will look for its included directories in order if it finds the header file in an earlier directory that it will use. Otherwise, it will eventually find a file of zero length and will be happy.

I assume that other cpp implementations are also looking for their include directories in that order.

+4
Sep 27 '08 at 4:31
source share

I had to do something similar for Symbian OS. Here's how I did it: let's say you want to check if the file "file_strange.h" exists, and you want to include some headers or a link to some libraries depending on the existence of this file.

first create a small batch file to verify the existence of this file.

autoconf is good, but more killing for many small projects.

---------- 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 ends

then i created a gnumake file

---------- 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 ends

include the check.mk file in your bld.inf file, it MUST be in front of your MMP files

 PRJ_MMPFILES gnumakefile checkmedialist.mk 

Now, at compile time, file_strange_supported.h will have the appropriate flag. you can use this flag in your cpp files or even in mmp file for example in mmp

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

and in .cpp

 #include "../inc/file_strange_supported.h" #ifdef NEW_API_SUPPORTED CStrangeApi* api = Api::NewLC(); #else // .. #endif 
+2
Jul 02 '09 at 11:38
source share



All Articles