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

标签 c++ gcc clang c-preprocessor header-files

我希望预处理器读入includesoflocalheaders,但忽略includesofsystemheaders换言之,如何让预处理器跳过表单的预处理指令:

#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() {}

本地include文件可能包含其他本地include文件,预处理器会递归地将它们全部包含进来;这与通常的做法非常相似它仍然会打印所有的系统文件头,但不会带来它们的内容。
在gcc上,到目前为止,我的调用是这样的:g++ -E -P main.cpp,其中-E在预处理之后停止,-P排除了行标记的生成。
我似乎找不到排除系统头处理的标志。

最佳答案

你愿意付出多少努力有一种令人讨厌的晦涩难懂的方法,但是它需要你设置一个虚拟目录来保存系统头的代理。哦,它不需要在任何源代码中做任何更改同样的技术同样适用于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 1488∶2011(不是在AFAIK版本之间发生了变化,而是逐字逐句地描述了C11标准中的内容,ISO/IEC 9899:2011也在第7.1.3节中):
§16.3宏替换
(8)如果一个#预处理令牌,后面跟着一个标识符,则在一个预处理指令开始的点出现词汇,该标识符不受宏替换的影响。
§16.3.4重新扫描和进一步更换
如果在扫描替换列表期间发现要替换的宏的名称(不包括源文件的其他预处理标记),则不替换该宏…
生成的完全宏替换的预处理标记序列不作为预处理指令处理,即使它类似于一个指令…
当预处理器遇到#include <iostream>时,它会在当前目录中查找,但找不到任何文件,然后在./system-headers中查找该文件,以便将其处理到输出中它包含一行,iostream由于include <iostream>是一个宏,因此它被扩展(到include),但会阻止进一步的扩展,并且由于第16.3.4条的规定,#include不作为指令处理因此,输出包含#
当预处理器遇到#include <iostream>时,它会在当前目录中查找文件并在输出中包含其内容。
冲洗并重复其他收割台如果#include "class_a.hpp"包含class_a.hpp,则最终再次扩展到#include <iostream>(带前导空格)如果您的#include <iostream>目录缺少任何头,那么预处理器将在正常位置搜索并查找并包含该头如果您使用编译器而不是直接使用system-headers,则可以禁止它在带有cpp的系统目录中查找,因此如果-nostdinc缺少(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() {}
$

请注意,生成代理项系统头非常容易:
for header in algorithm chrono iostream string …
do echo "include <$header>" > system-headers/$header
done

JFTR,测试是在MacOSX10.11.5和GCC6.1.0上完成的如果您使用的是GCC(GNU编译器集合,具有领先的示例编译器system-headersgcc),那么您的里程数应该不会因任何可能的替代版本而有太大的变化。
如果您不喜欢使用宏名g++,可以将其更改为任何其他适合您的名称-includesyzygyapoplexynadir,…-并将代理项头更改为使用该名称,然后在预处理器(编译器)命令行上定义该名称reinclude的一个优点是,不太可能有任何东西将其用作宏名。
自动生成代理项头
osgxasks
如何自动生成模拟系统头?
有多种选择一种方法是分析代码(例如使用include来查找引用的或可能引用的名称,并生成适当的代理项头如果生成一些未使用的头,这无关紧要—它们不会影响进程注意,如果使用grep,代理项必须是#include <sys/wait.h>;这会使所示的shell代码稍微复杂一些,但不会复杂很多另一种方法是查看系统头目录(./system-headers/sys/wait.h/usr/include等)中的头,并为在那里找到的头生成代理项。
例如,/usr/local/include可能是:
#!/bin/sh

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

我们可以编写mksurrogates.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
$

这假设头文件名不包含空格,这是不合理的-这将是一个勇敢的程序员谁创建了头文件名与空格或其他棘手的字符。
listsyshdrs.sh的完整生产就绪版本将接受指定代理项头目录的参数。

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

相关文章:

c++ - 我如何检查迭代器是否到达列表末尾?

c++ - 在基于范围的 for 循环中报告奇怪的语法错误

c - 强制 64 位长 double ?

iphone - 无法在 iPhone 上编译/汇编 MRC 和 MCR 协处理器指令?

c - 在 Clang 中使用带有 qsort 的 block 时出现不兼容的指针类型错误

c++ - 为多次执行创建makefile

c# - C++等价物。 C# 的 intQueue.Contains()

c++ - 为什么 C++ 链接器在构建期间需要库文件,即使我是动态链接的?

c++ - 奇怪的 C/C++ 语法

c++ - 为什么隐式转换不应用于模板化函数参数?