如何仅在本地标头上运行预处理器?

时间:2021-08-13 15:05:58

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.

在gcc上,我的调用到目前为止看起来像这样:g ++ -E -P main.cpp,其中-E在预处理后停止,-P排除行标记的生成。我似乎无法找到排除系统标头处理的标志。

3 个解决方案

#1


17  

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.

你愿意去多少努力?有一种令人讨厌的模糊方法,但它需要你设置一个虚拟目录来保存系统头的代理。 OTOH,它不需要对任何源代码进行任何更改。相同的技术同样适用于C代码。

Setup

Files:

./class_a.hpp
./class_b.hpp
./example.cpp
./system-headers/iostream
./system-headers/string

The 'system headers' such as ./system-headers/iostream contain a single line (there is no # on that line!):

诸如./system-headers/iostream之类的“系统头”包含一行(该行上没有#!):

include <iostream>

The class headers each contain a single line like:

每个类头都包含一行,如:

class A{};

The contents of example.cpp are what you show in the question:

example.cpp的内容是您在问题中显示的内容:

#include <iostream>     //system
#include "class_a.hpp"  //local
#include <string>       //system
#include "class_b.hpp"  //local

int main() {}

Running the C preprocessor

Running the C preprocessor like this produces the output shown:

像这样运行C预处理器会产生显示的输出:

$ cpp -Dinclude=#include -I. -Isystem-headers example.cpp
# 1 "example.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "example.cpp"
# 1 "system-headers/iostream" 1
 #include <iostream>
# 2 "example.cpp" 2
# 1 "class_a.hpp" 1
class A{};
# 3 "example.cpp" 2
# 1 "system-headers/string" 1
 #include <string>
# 4 "example.cpp" 2
# 1 "class_b.hpp" 1
class B{};
# 5 "example.cpp" 2

int main() {}
$

If you eliminate the # n lines, that output is:

如果消除#n行,则输出为:

$ cpp -Dinclude=#include -I. -Isystem-headers example.cpp | grep -v '^# [0-9]'
 #include <iostream>
class A{};
 #include <string>
class B{};

int main() {}
$

which, give or take the space at the beginning of the lines containing #include, is what you wanted.

在包含#include的行的开头,给出或占用空间是你想要的。

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):

-Dinclude = #include参数等同于#define include #include。当预处理器从宏生成输出时,即使它看起来像指令(例如#include),它也不是预处理器指令。引用C ++ 11标准ISO / IEC 14882:2011(并非在AFAIK版本之间发生了变化 - 并且,在C11标准中也是如此,ISO / IEC 9899:2011也是如此,在§6.10.3中) :

§16.3 Macro replacement

¶8 If a # preprocessing token, followed by an identifier, occurs lexically at the point at which a preprocessing directive could begin, the identifier is not subject to macro replacement.

¶8如果#预处理标记后跟一个标识符,则在预处理指令可以开始的位置出现词汇,标识符不受宏替换的影响。

§16.3.4 Rescanning and further replacement

§16.3.4重新扫描和进一步更换

¶2 If the name of the macro being replaced is found during this scan of the replacement list (not including the rest of the source file’s preprocessing tokens), it is not replaced. …

¶2如果在替换列表的扫描期间(不包括源文件的其余预处理标记)找到要替换的宏的名称,则不会替换它。 ...

¶3 The resulting completely macro-replaced preprocessing token sequence is not processed as a preprocessing directive even if it resembles one, …

¶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 file iostream so it processes that into the output. It contains a single line, include <iostream>. Since include 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>.

当预处理器遇到#include 时,它会查找当前目录并找不到文件,然后查找./system-headers并找到文件iostream,以便将其处理到输出中。它包含一行,包括 。由于include是一个宏,它被扩展(到#include)但是防止了进一步扩展,并且由于§16.3.4¶3,#不作为指令处理。因此,输出包含#include

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.

当预处理器遇到#include“class_a.hpp”时,它会查找当前目录并找到该文件并将其内容包含在输出中。

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 your system-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 than cpp directly, you can prohibit it from looking in the system directories with -nostdinc — so the preprocessor will generate an error if system-headers is missing a (surrogate for a) system header.

冲洗并重复其他标题。如果class_a.hpp包含#include ,那么最终会再次扩展到#include (带有前导空格)。如果您的system-headers目录缺少任何标头,则预处理器将在正常位置搜索并查找并包含该标头。如果直接使用编译器而不是cpp,则可以禁止它使用-nostdinc查看系统目录 - 因此如果system-headers缺少(代理a)系统头,预处理器将生成错误。

$ g++ -E -nostdinc -Dinclude=#include -I. -Isystem-headers example.cpp | grep -v '^# [0-9]'
 #include <iostream>
class A{};
 #include <string>
class B{};

int main() {}
$

Note that it is very easy to generate the surrogate system headers:

请注意,生成代理系统标头非常容易:

for header in algorithm chrono iostream string …
do echo "include <$header>" > system-headers/$header
done

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 and g++), your mileage shouldn't vary very much with any plausible alternative version.

JFTR,测试是在使用GCC 6.1.0的Mac OS X 10.11.5上完成的。如果您正在使用GCC(GNU编译器集合,带有领先的示例编译器gcc和g ++),那么您的里程数不应与任何合理的替代版本有很大差异。

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 of include is that it's improbable that you have anything using that as a macro name.

如果您对使用宏名称include感到不舒服,可以将其更改为适合您的任何其他内容 - syzygy,apoplexy,nadir,reinclude,... - 并更改代理标头以使用该名称,并在预处理器上定义该名称(编译器)命令行。包含的一个优点是,您不可能将其用作宏名称。

Automatically generating surrogate headers

osgx asks:

How can we automate the generation of mock system headers?

我们如何自动生成模拟系统头文件?

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:

有多种选择。一种是分析您的代码(例如使用grep)来查找引用或可能引用的名称并生成相应的代理标头。如果生成一些未使用的标头并不重要 - 它们不会影响该过程。请注意,如果使用#include ,则代理必须是./system-headers/sys/wait.h;这有点使所显示的shell代码复杂化,但不是很多。另一种方法是查看系统头目录(/ usr / include,/ usr / local / include等)中的头文件,并为您在那里找到的头文件生成代理。例如,mksurrogates.sh可能是:

#!/bin/sh

sysdir="./system-headers"
for header in "$@"
do
    mkdir -p "$sysdir/$(dirname $header)"
    echo "include <$header>" > "$sysdir/$header"
done

And we can write listsyshdrs.sh to find the system headers referenced in source code under a named directory:

我们可以编写listsyshdrs.sh来查找命名目录下源代码中引用的系统头:

#!/bin/sh

grep -h -e '^[[:space:]]*#[[:space:]]*include[[:space:]]*<[^>]*>' -r "${@:-.}" |
sed 's/^[[:space:]]*#[[:space:]]*include[[:space:]]*<\([^>]*\)>.*/\1/' |
sort -u

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问题的答案扫描源树时,生成了这样的标题列表:

algorithm         arpa/inet.h       assert.h          cassert
chrono            cmath             cstddef           cstdint
cstdlib           cstring           ctime             ctype.h
dirent.h          errno.h           fcntl.h           float.h
getopt.h          inttypes.h        iomanip           iostream
limits.h          locale.h          map               math.h
memory.h          netdb.h           netinet/in.h      pthread.h
semaphore.h       signal.h          sstream           stdarg.h
stdbool.h         stddef.h          stdint.h          stdio.h
stdlib.h          string            string.h          sys/ipc.h
sys/mman.h        sys/param.h       sys/ptrace.h      sys/select.h
sys/sem.h         sys/shm.h         sys/socket.h      sys/stat.h
sys/time.h        sys/timeb.h       sys/times.h       sys/types.h
sys/wait.h        termios.h         time.h            unistd.h
utility           vector            wchar.h

So, to generate the surrogates for the source tree under the current directory:

因此,要在当前目录下生成源树的代理:

$ sh mksurrogatehdr.sh $(sh listsyshdrs.sh)
$ ls -lR system-headers
total 344
-rw-r--r--   1 jleffler  staff   20 Jul  2 17:27 algorithm
drwxr-xr-x   3 jleffler  staff  102 Jul  2 17:27 arpa
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 assert.h
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 cassert
-rw-r--r--   1 jleffler  staff   17 Jul  2 17:27 chrono
-rw-r--r--   1 jleffler  staff   16 Jul  2 17:27 cmath
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 cstddef
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 cstdint
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 cstdlib
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 cstring
-rw-r--r--   1 jleffler  staff   16 Jul  2 17:27 ctime
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 ctype.h
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 dirent.h
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 errno.h
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 fcntl.h
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 float.h
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 getopt.h
-rw-r--r--   1 jleffler  staff   21 Jul  2 17:27 inttypes.h
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 iomanip
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 iostream
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 limits.h
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 locale.h
-rw-r--r--   1 jleffler  staff   14 Jul  2 17:27 map
-rw-r--r--   1 jleffler  staff   17 Jul  2 17:27 math.h
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 memory.h
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 netdb.h
drwxr-xr-x   3 jleffler  staff  102 Jul  2 17:27 netinet
-rw-r--r--   1 jleffler  staff   20 Jul  2 17:27 pthread.h
-rw-r--r--   1 jleffler  staff   22 Jul  2 17:27 semaphore.h
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 signal.h
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 sstream
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 stdarg.h
-rw-r--r--   1 jleffler  staff   20 Jul  2 17:27 stdbool.h
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 stddef.h
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 stdint.h
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 stdio.h
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 stdlib.h
-rw-r--r--   1 jleffler  staff   17 Jul  2 17:27 string
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 string.h
drwxr-xr-x  16 jleffler  staff  544 Jul  2 17:27 sys
-rw-r--r--   1 jleffler  staff   20 Jul  2 17:27 termios.h
-rw-r--r--   1 jleffler  staff   17 Jul  2 17:27 time.h
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 unistd.h
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 utility
-rw-r--r--   1 jleffler  staff   17 Jul  2 17:27 vector
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 wchar.h

system-headers/arpa:
total 8
-rw-r--r--  1 jleffler  staff  22 Jul  2 17:27 inet.h

system-headers/netinet:
total 8
-rw-r--r--  1 jleffler  staff  23 Jul  2 17:27 in.h

system-headers/sys:
total 112
-rw-r--r--  1 jleffler  staff  20 Jul  2 17:27 ipc.h
-rw-r--r--  1 jleffler  staff  21 Jul  2 17:27 mman.h
-rw-r--r--  1 jleffler  staff  22 Jul  2 17:27 param.h
-rw-r--r--  1 jleffler  staff  23 Jul  2 17:27 ptrace.h
-rw-r--r--  1 jleffler  staff  23 Jul  2 17:27 select.h
-rw-r--r--  1 jleffler  staff  20 Jul  2 17:27 sem.h
-rw-r--r--  1 jleffler  staff  20 Jul  2 17:27 shm.h
-rw-r--r--  1 jleffler  staff  23 Jul  2 17:27 socket.h
-rw-r--r--  1 jleffler  staff  21 Jul  2 17:27 stat.h
-rw-r--r--  1 jleffler  staff  21 Jul  2 17:27 time.h
-rw-r--r--  1 jleffler  staff  22 Jul  2 17:27 timeb.h
-rw-r--r--  1 jleffler  staff  22 Jul  2 17:27 times.h
-rw-r--r--  1 jleffler  staff  22 Jul  2 17:27 types.h
-rw-r--r--  1 jleffler  staff  21 Jul  2 17:27 wait.h
$

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.

完整的生产就绪版本的mksurrogates.sh将接受指定代理标头目录的参数。

#2


2  

With clang you can do e.g.:

用clang你可以做到例如:

 clang -Imyinclude -P -E -nostdinc -nobuiltininc main.cpp

There does not seem to be a way to preserve the system #include lines it cannot find though.

似乎没有办法保留它找不到的系统#include行。

This doesn't work for gcc, as its preprocessor will stop when using -nostdinc and it can't find an #included header file.

这对gcc不起作用,因为它的预处理器在使用-nostdinc时会停止,并且找不到#included头文件。

#3


1  

You could put a #define SYSTEM_HEADERS 0 in a configuration header and do it like this

您可以在配置头中放置#define SYSTEM_HEADERS 0,并按照这样做

#include "config.h" // the configuration header
#include "class_a.hpp"
#include "class_b.hpp"

#if SYSTEM_HEADERS // which is #if 0
#include <iostream>
#include <string>
#endif

and when you want system headers you could make it #define SYSTEM_HEADERS 1 which will include system headers.

当你想要系统头文件时,你可以使它#define SYSTEM_HEADERS 1,它将包括系统头文件。

#1


17  

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.

你愿意去多少努力?有一种令人讨厌的模糊方法,但它需要你设置一个虚拟目录来保存系统头的代理。 OTOH,它不需要对任何源代码进行任何更改。相同的技术同样适用于C代码。

Setup

Files:

./class_a.hpp
./class_b.hpp
./example.cpp
./system-headers/iostream
./system-headers/string

The 'system headers' such as ./system-headers/iostream contain a single line (there is no # on that line!):

诸如./system-headers/iostream之类的“系统头”包含一行(该行上没有#!):

include <iostream>

The class headers each contain a single line like:

每个类头都包含一行,如:

class A{};

The contents of example.cpp are what you show in the question:

example.cpp的内容是您在问题中显示的内容:

#include <iostream>     //system
#include "class_a.hpp"  //local
#include <string>       //system
#include "class_b.hpp"  //local

int main() {}

Running the C preprocessor

Running the C preprocessor like this produces the output shown:

像这样运行C预处理器会产生显示的输出:

$ cpp -Dinclude=#include -I. -Isystem-headers example.cpp
# 1 "example.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "example.cpp"
# 1 "system-headers/iostream" 1
 #include <iostream>
# 2 "example.cpp" 2
# 1 "class_a.hpp" 1
class A{};
# 3 "example.cpp" 2
# 1 "system-headers/string" 1
 #include <string>
# 4 "example.cpp" 2
# 1 "class_b.hpp" 1
class B{};
# 5 "example.cpp" 2

int main() {}
$

If you eliminate the # n lines, that output is:

如果消除#n行,则输出为:

$ cpp -Dinclude=#include -I. -Isystem-headers example.cpp | grep -v '^# [0-9]'
 #include <iostream>
class A{};
 #include <string>
class B{};

int main() {}
$

which, give or take the space at the beginning of the lines containing #include, is what you wanted.

在包含#include的行的开头,给出或占用空间是你想要的。

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):

-Dinclude = #include参数等同于#define include #include。当预处理器从宏生成输出时,即使它看起来像指令(例如#include),它也不是预处理器指令。引用C ++ 11标准ISO / IEC 14882:2011(并非在AFAIK版本之间发生了变化 - 并且,在C11标准中也是如此,ISO / IEC 9899:2011也是如此,在§6.10.3中) :

§16.3 Macro replacement

¶8 If a # preprocessing token, followed by an identifier, occurs lexically at the point at which a preprocessing directive could begin, the identifier is not subject to macro replacement.

¶8如果#预处理标记后跟一个标识符,则在预处理指令可以开始的位置出现词汇,标识符不受宏替换的影响。

§16.3.4 Rescanning and further replacement

§16.3.4重新扫描和进一步更换

¶2 If the name of the macro being replaced is found during this scan of the replacement list (not including the rest of the source file’s preprocessing tokens), it is not replaced. …

¶2如果在替换列表的扫描期间(不包括源文件的其余预处理标记)找到要替换的宏的名称,则不会替换它。 ...

¶3 The resulting completely macro-replaced preprocessing token sequence is not processed as a preprocessing directive even if it resembles one, …

¶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 file iostream so it processes that into the output. It contains a single line, include <iostream>. Since include 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>.

当预处理器遇到#include 时,它会查找当前目录并找不到文件,然后查找./system-headers并找到文件iostream,以便将其处理到输出中。它包含一行,包括 。由于include是一个宏,它被扩展(到#include)但是防止了进一步扩展,并且由于§16.3.4¶3,#不作为指令处理。因此,输出包含#include

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.

当预处理器遇到#include“class_a.hpp”时,它会查找当前目录并找到该文件并将其内容包含在输出中。

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 your system-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 than cpp directly, you can prohibit it from looking in the system directories with -nostdinc — so the preprocessor will generate an error if system-headers is missing a (surrogate for a) system header.

冲洗并重复其他标题。如果class_a.hpp包含#include ,那么最终会再次扩展到#include (带有前导空格)。如果您的system-headers目录缺少任何标头,则预处理器将在正常位置搜索并查找并包含该标头。如果直接使用编译器而不是cpp,则可以禁止它使用-nostdinc查看系统目录 - 因此如果system-headers缺少(代理a)系统头,预处理器将生成错误。

$ g++ -E -nostdinc -Dinclude=#include -I. -Isystem-headers example.cpp | grep -v '^# [0-9]'
 #include <iostream>
class A{};
 #include <string>
class B{};

int main() {}
$

Note that it is very easy to generate the surrogate system headers:

请注意,生成代理系统标头非常容易:

for header in algorithm chrono iostream string …
do echo "include <$header>" > system-headers/$header
done

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 and g++), your mileage shouldn't vary very much with any plausible alternative version.

JFTR,测试是在使用GCC 6.1.0的Mac OS X 10.11.5上完成的。如果您正在使用GCC(GNU编译器集合,带有领先的示例编译器gcc和g ++),那么您的里程数不应与任何合理的替代版本有很大差异。

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 of include is that it's improbable that you have anything using that as a macro name.

如果您对使用宏名称include感到不舒服,可以将其更改为适合您的任何其他内容 - syzygy,apoplexy,nadir,reinclude,... - 并更改代理标头以使用该名称,并在预处理器上定义该名称(编译器)命令行。包含的一个优点是,您不可能将其用作宏名称。

Automatically generating surrogate headers

osgx asks:

How can we automate the generation of mock system headers?

我们如何自动生成模拟系统头文件?

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:

有多种选择。一种是分析您的代码(例如使用grep)来查找引用或可能引用的名称并生成相应的代理标头。如果生成一些未使用的标头并不重要 - 它们不会影响该过程。请注意,如果使用#include ,则代理必须是./system-headers/sys/wait.h;这有点使所显示的shell代码复杂化,但不是很多。另一种方法是查看系统头目录(/ usr / include,/ usr / local / include等)中的头文件,并为您在那里找到的头文件生成代理。例如,mksurrogates.sh可能是:

#!/bin/sh

sysdir="./system-headers"
for header in "$@"
do
    mkdir -p "$sysdir/$(dirname $header)"
    echo "include <$header>" > "$sysdir/$header"
done

And we can write listsyshdrs.sh to find the system headers referenced in source code under a named directory:

我们可以编写listsyshdrs.sh来查找命名目录下源代码中引用的系统头:

#!/bin/sh

grep -h -e '^[[:space:]]*#[[:space:]]*include[[:space:]]*<[^>]*>' -r "${@:-.}" |
sed 's/^[[:space:]]*#[[:space:]]*include[[:space:]]*<\([^>]*\)>.*/\1/' |
sort -u

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问题的答案扫描源树时,生成了这样的标题列表:

algorithm         arpa/inet.h       assert.h          cassert
chrono            cmath             cstddef           cstdint
cstdlib           cstring           ctime             ctype.h
dirent.h          errno.h           fcntl.h           float.h
getopt.h          inttypes.h        iomanip           iostream
limits.h          locale.h          map               math.h
memory.h          netdb.h           netinet/in.h      pthread.h
semaphore.h       signal.h          sstream           stdarg.h
stdbool.h         stddef.h          stdint.h          stdio.h
stdlib.h          string            string.h          sys/ipc.h
sys/mman.h        sys/param.h       sys/ptrace.h      sys/select.h
sys/sem.h         sys/shm.h         sys/socket.h      sys/stat.h
sys/time.h        sys/timeb.h       sys/times.h       sys/types.h
sys/wait.h        termios.h         time.h            unistd.h
utility           vector            wchar.h

So, to generate the surrogates for the source tree under the current directory:

因此,要在当前目录下生成源树的代理:

$ sh mksurrogatehdr.sh $(sh listsyshdrs.sh)
$ ls -lR system-headers
total 344
-rw-r--r--   1 jleffler  staff   20 Jul  2 17:27 algorithm
drwxr-xr-x   3 jleffler  staff  102 Jul  2 17:27 arpa
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 assert.h
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 cassert
-rw-r--r--   1 jleffler  staff   17 Jul  2 17:27 chrono
-rw-r--r--   1 jleffler  staff   16 Jul  2 17:27 cmath
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 cstddef
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 cstdint
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 cstdlib
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 cstring
-rw-r--r--   1 jleffler  staff   16 Jul  2 17:27 ctime
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 ctype.h
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 dirent.h
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 errno.h
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 fcntl.h
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 float.h
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 getopt.h
-rw-r--r--   1 jleffler  staff   21 Jul  2 17:27 inttypes.h
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 iomanip
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 iostream
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 limits.h
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 locale.h
-rw-r--r--   1 jleffler  staff   14 Jul  2 17:27 map
-rw-r--r--   1 jleffler  staff   17 Jul  2 17:27 math.h
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 memory.h
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 netdb.h
drwxr-xr-x   3 jleffler  staff  102 Jul  2 17:27 netinet
-rw-r--r--   1 jleffler  staff   20 Jul  2 17:27 pthread.h
-rw-r--r--   1 jleffler  staff   22 Jul  2 17:27 semaphore.h
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 signal.h
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 sstream
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 stdarg.h
-rw-r--r--   1 jleffler  staff   20 Jul  2 17:27 stdbool.h
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 stddef.h
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 stdint.h
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 stdio.h
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 stdlib.h
-rw-r--r--   1 jleffler  staff   17 Jul  2 17:27 string
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 string.h
drwxr-xr-x  16 jleffler  staff  544 Jul  2 17:27 sys
-rw-r--r--   1 jleffler  staff   20 Jul  2 17:27 termios.h
-rw-r--r--   1 jleffler  staff   17 Jul  2 17:27 time.h
-rw-r--r--   1 jleffler  staff   19 Jul  2 17:27 unistd.h
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 utility
-rw-r--r--   1 jleffler  staff   17 Jul  2 17:27 vector
-rw-r--r--   1 jleffler  staff   18 Jul  2 17:27 wchar.h

system-headers/arpa:
total 8
-rw-r--r--  1 jleffler  staff  22 Jul  2 17:27 inet.h

system-headers/netinet:
total 8
-rw-r--r--  1 jleffler  staff  23 Jul  2 17:27 in.h

system-headers/sys:
total 112
-rw-r--r--  1 jleffler  staff  20 Jul  2 17:27 ipc.h
-rw-r--r--  1 jleffler  staff  21 Jul  2 17:27 mman.h
-rw-r--r--  1 jleffler  staff  22 Jul  2 17:27 param.h
-rw-r--r--  1 jleffler  staff  23 Jul  2 17:27 ptrace.h
-rw-r--r--  1 jleffler  staff  23 Jul  2 17:27 select.h
-rw-r--r--  1 jleffler  staff  20 Jul  2 17:27 sem.h
-rw-r--r--  1 jleffler  staff  20 Jul  2 17:27 shm.h
-rw-r--r--  1 jleffler  staff  23 Jul  2 17:27 socket.h
-rw-r--r--  1 jleffler  staff  21 Jul  2 17:27 stat.h
-rw-r--r--  1 jleffler  staff  21 Jul  2 17:27 time.h
-rw-r--r--  1 jleffler  staff  22 Jul  2 17:27 timeb.h
-rw-r--r--  1 jleffler  staff  22 Jul  2 17:27 times.h
-rw-r--r--  1 jleffler  staff  22 Jul  2 17:27 types.h
-rw-r--r--  1 jleffler  staff  21 Jul  2 17:27 wait.h
$

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.

完整的生产就绪版本的mksurrogates.sh将接受指定代理标头目录的参数。

#2


2  

With clang you can do e.g.:

用clang你可以做到例如:

 clang -Imyinclude -P -E -nostdinc -nobuiltininc main.cpp

There does not seem to be a way to preserve the system #include lines it cannot find though.

似乎没有办法保留它找不到的系统#include行。

This doesn't work for gcc, as its preprocessor will stop when using -nostdinc and it can't find an #included header file.

这对gcc不起作用,因为它的预处理器在使用-nostdinc时会停止,并且找不到#included头文件。

#3


1  

You could put a #define SYSTEM_HEADERS 0 in a configuration header and do it like this

您可以在配置头中放置#define SYSTEM_HEADERS 0,并按照这样做

#include "config.h" // the configuration header
#include "class_a.hpp"
#include "class_b.hpp"

#if SYSTEM_HEADERS // which is #if 0
#include <iostream>
#include <string>
#endif

and when you want system headers you could make it #define SYSTEM_HEADERS 1 which will include system headers.

当你想要系统头文件时,你可以使它#define SYSTEM_HEADERS 1,它将包括系统头文件。