gpt4 book ai didi

c++ - 如何仅在本地 header 上运行预处理器?

转载 作者:IT老高 更新时间:2023-10-28 21:46:46 25 4
gpt4 key购买 nike

我希望预处理器读取本地头文件的包含,但忽略系统头文件的包含。换句话说,我如何让预处理器跳过以下形式的预处理指令:

#include <h-char-sequence> new-line

但仍然处理以下形式的指令:
#include "q-char-sequence" new-line

作为代码示例,请观察以下文件:
#include <iostream>     //system
#include "class_a.hpp" //local
#include <string> //system
#include "class_b.hpp" //local

int main() {}

我怎样才能得到预处理器的输出是:
#include <iostream>
class A{};
#include <string>
class B{};

int main() {}

本地包含文件可能包含其他本地包含文件,预处理器会递归地将它们全部引入;就像它通常所做的那样。它仍然会打印所有系统文件头,但不会引入它们的内容。

在 gcc 上,到目前为止我的电话看起来像这样: g++ -E -P main.cpp ,其中 -E预处理后停止, -P不包括线标记的生成。
我似乎找不到排除系统 header 处理的标志。

最佳答案

你愿意付出多少努力?有一种令人讨厌的晦涩方法可以做到这一点,但它需要您设置一个虚拟目录来保存系统标题的代理。 OTOH,它不需要对您的任何源代码进行任何更改。相同的技术同样适用于 C 代码。
设置
文件:

./class_a.hpp
./class_b.hpp
./example.cpp
./system-headers/iostream
./system-headers/string
“系统标题”,例如 ./system-headers/iostream包含一行(该行没有 #!):
include <iostream>
每个类标题都包含一行,例如:
class A{};
example.cpp的内容是您在问题中显示的内容:
#include <iostream>     //system
#include "class_a.hpp" //local
#include <string> //system
#include "class_b.hpp" //local

int main() {}
运行 C 预处理器
像这样运行 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() {}
$
如果消除 # n行,该输出是:
$ cpp -Dinclude=#include -I. -Isystem-headers example.cpp | grep -v '^# [0-9]'
#include <iostream>
class A{};
#include <string>
class B{};

int main() {}
$
其中,在包含 #include 的行的开头给或取空格,就是你想要的。
分析 -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.

§16.3.4 Rescanning and further replacement

¶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. …

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


当预处理器遇到 #include <iostream> ,它在当前目录中查找没有找到文件,然后在 ./system-headers中查找并找到文件 iostream所以它把它处理成输出。它包含一行, include <iostream> .自 include是一个宏,它被扩展(到 #include )但进一步扩展被阻止,并且 #由于 §16.3.4 ¶3,不作为指令处理。因此,输出包含 #include <iostream> .
当预处理器遇到 #include "class_a.hpp" ,它会在当前目录中查找并找到该文件并将其内容包含在输出中。
冲洗并重复其他标题。如 class_a.hpp#include <iostream> ,然后最终扩展到 #include <iostream>再次(带前导空格)。如果您的 system-headers目录缺少任何标题,然后预处理器将在正常位置搜索并找到并包含它。如果你使用编译器而不是 cpp可以直接用 -nostdinc禁止查看系统目录。 — 所以如果 system-headers,预处理器会产生一个错误缺少(a 的代理)系统 header 。
$ g++ -E -nostdinc -Dinclude=#include -I. -Isystem-headers example.cpp | grep -v '^# [0-9]'
#include <iostream>
class A{};
#include <string>
class B{};

int main() {}
$
请注意,生成代理系统 header 非常容易:
for header in algorithm chrono iostream string …
do echo "include <$header>" > system-headers/$header
done
JFTR,测试是在 Mac OS X 10.11.5 和 GCC 6.1.0 上完成的。如果您使用 GCC(GNU 编译器集合,带有领先的示例编译器 gccg++),您的里程不应该与任何合理的替代版本有很大差异。
如果您不喜欢使用宏名称 include ,您可以将其更改为适合您的任何其他内容 — syzygy , apoplexy , nadir , reinclude , … — 并更改代理 header 以使用该名称,并在预处理器(编译器)命令行上定义该名称。 include的优势之一是不可能有任何东西使用它作为宏名称。
自动生成代理标题
osgx asks :

How can we automate the generation of mock system headers?


有多种选择。一种是分析您的代码(例如 grep)以查找被或可能被引用的名称并生成适当的代理 header 。如果您生成一些未使用的 header 并不重要 - 它们不会影响过程。请注意,如果您使用 #include <sys/wait.h> ,代理必须是 ./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
我们可以写 listsyshdrs.sh在命名目录下查找源代码中引用的系统头文件:
#!/bin/sh

grep -h -e '^[[:space:]]*#[[:space:]]*include[[:space:]]*<[^>]*>' -r "${@:-.}" |
sed 's/^[[:space:]]*#[[:space:]]*include[[:space:]]*<\([^>]*\)>.*/\1/' |
sort -u
添加了一些格式后,当我用我对 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
因此,要为当前目录下的源树生成代理:
$ 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
$
这假设头文件名不包含空格,这并非不合理——用空格或其他棘手字符创建头文件名是一个勇敢的程序员。 mksurrogates.sh 的完整生产就绪版本将接受指定代理头目录的参数。

关于c++ - 如何仅在本地 header 上运行预处理器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20889460/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com