I want the preprocessor to read in the includes of local headers, but ignore the includes of system headers. To put it another way, how do I get the preprocessor to skip over preprocessing directives of the form:
#include <h-char-sequence> new-line
but still process directives of the form:
#include "q-char-sequence" new-line
As a code example, observe the following file:
#include <iostream> //system
#include "class_a.hpp" //local
#include <string> //system
#include "class_b.hpp" //local
int main() {}
how can I get the output of the preprocessor to be:
#include <iostream>
class A{};
#include <string>
class B{};
int main() {}
Local include files may include other local include files, and the preprocessor would recursively bring them all in; much like it normally does. It would still print all of the system file headers, but it would not bring in their contents.
on gcc, my call looks like this so far: g++ -E -P main.cpp
, where -E
stops after preprocessing, and -P
excludes the generation of line markers.
I can't seem to find a flag that excludes the processing of system headers.
You could put a
#define SYSTEM_HEADERS 0
in a configuration header and do it like thisand when you want system headers you could make it
#define SYSTEM_HEADERS 1
which will include system headers.How much effort are you willing to go to? There's an obnoxiously obscure way to do it but it requires you to set up a dummy directory to hold surrogates for the system headers. OTOH, it doesn't require any changes in any of your source code. The same technique works equally well for C code.
Setup
Files:
The 'system headers' such as
./system-headers/iostream
contain a single line (there is no#
on that line!):The class headers each contain a single line like:
The contents of
example.cpp
are what you show in the question:Running the C preprocessor
Running the C preprocessor like this produces the output shown:
If you eliminate the
# n
lines, that output is:which, give or take the space at the beginning of the lines containing
#include
, is what you wanted.Analysis
The
-Dinclude=#include
argument is equivalent to#define include #include
. When the preprocessor generates output from a macro, even if it looks like a directive (such as#include
), it is not a preprocessor directive. Quoting the C++11 standard ISO/IEC 14882:2011 (not that this has changed between versions AFAIK — and is, verbatim, what it says in the C11 standard, ISO/IEC 9899:2011 too, in §6.10.3):When the preprocessor encounters
#include <iostream>
, it looks in the current directory and finds no file, then looks in./system-headers
and finds the fileiostream
so it processes that into the output. It contains a single line,include <iostream>
. Sinceinclude
is a macro, it is expanded (to#include
) but further expansion is prevented, and the#
is not processed as a directive because of §16.3.4 ¶3. Thus, the output contains#include <iostream>
.When the preprocessor encounters
#include "class_a.hpp"
, it looks in the current directory and finds the file and includes its contents in the output.Rinse and repeat for the other headers. If
class_a.hpp
contained#include <iostream>
, then that ends up expanding to#include <iostream>
again (with the leading space). If yoursystem-headers
directory is missing any header, then the preprocessor will search in the normal locations and find and include that. If you use the compiler rather thancpp
directly, you can prohibit it from looking in the system directories with-nostdinc
— so the preprocessor will generate an error ifsystem-headers
is missing a (surrogate for a) system header.Note that it is very easy to generate the surrogate system headers:
JFTR, testing was done on Mac OS X 10.11.5 with GCC 6.1.0. If you're using GCC (the GNU Compiler Collection, with leading example compilers
gcc
andg++
), your mileage shouldn't vary very much with any plausible alternative version.If you're uncomfortable using the macro name
include
, you can change it to anything else that suits you —syzygy
,apoplexy
,nadir
,reinclude
, … — and change the surrogate headers to use that name, and define that name on the preprocessor (compiler) command line. One advantage ofinclude
is that it's improbable that you have anything using that as a macro name.Automatically generating surrogate headers
osgx asks:
There are a variety of options. One is to analyze your code (with
grep
for example) to find the names that are, or might be, referenced and generate the appropriate surrogate headers. It doesn't matter if you generate a few unused headers — they won't affect the process. Note that if you use#include <sys/wait.h>
, the surrogate must be./system-headers/sys/wait.h
; that slightly complicates the shell code shown, but not by very much. Another way would look at the headers in the system header directories (/usr/include
,/usr/local/include
, etc) and generate surrogates for the headers you find there. For example,mksurrogates.sh
might be:And we can write
listsyshdrs.sh
to find the system headers referenced in source code under a named directory:With a bit of formatting added, that generated a list of headers like this when I scanned the source tree with my answers to SO questions:
So, to generate the surrogates for the source tree under the current directory:
This assumes that header file names contain no spaces, which is not unreasonable — it would be a brave programmer who created header file names with spaces or other tricky characters.
A full production-ready version of
mksurrogates.sh
would accept an argument specifying the surrogate header directory.With clang you can do e.g.:
There does not seem to be a way to preserve the system
#include
lines it cannot find though.This doesn't work for gcc, as its preprocessor will stop when using
-nostdinc
and it can't find an#included
header file.