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

特雷弗·希基(Trevor Hickey)

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

#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不包含行标记的生成。
我似乎找不到一个排除系统头文件处理的标志。

乔纳森·莱夫勒

您愿意付出多少努力?有一种令人费解的方法,但是它要求您设置一个虚拟目录来保存系统头文件的替代项。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宏替换

¶8如果#在预处理指令可以开始的地方按词法出现预处理标记和标识符,则该标识符将不受宏替换。

第16.3.4条重新扫描和进一步更换

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

¶3所产生的完全由宏替换的预处理令牌序列不会被作为预处理指令处理,即使它类似于一个……

当预处理器遇到时#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缺少系统头文件(替代),则预处理器将生成错误

$ 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,测试是在带有GCC 6.1.0的Mac OS X 10.11.5上完成的。如果您使用的是GCC(带有领先的示例编译器gcc的GNU编译器集合g++),那么任何可行的替代版本的里程都不会有太大变化。

如果您在使用宏的名称是不舒服include,你可以把它改成其他任何适合你- ,syzygyapoplexy ,... -和改变代理头使用该名称,并定义预处理器(编译器)命令行上的那个名字。优点之一是,您不可能将其用作宏名。nadirreincludeinclude

自动生成代理标头

osgx

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

有多种选择。一种方法是分析您的代码(grep例如使用)来查找被引用或可能被引用的名称,并生成适当的代理标头。生成一些未使用的标头并不重要-它们不会影响该过程。请注意,如果使用#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将接受指定代理标头目录的参数。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

如何使用预处理器计算日志

来自分类Dev

预处理器更换

来自分类Dev

如何使用升压预处理器运行一系列功能?

来自分类Dev

如何使用boost预处理器生成访问器?

来自分类Dev

如何用if语句替换预处理器宏?

来自分类Dev

C ++预处理器

来自分类Dev

如何从命令行运行Sage预处理器

来自分类Dev

如何在gcc程序集中启用预处理器

来自分类Dev

如何在msvc预处理器中打印宏?

来自分类Dev

如何实现打孔自动前缀预处理器?

来自分类Dev

如何使预处理器宏贪婪?

来自分类Dev

如何使用Clang作为预处理器

来自分类Dev

如何测试C预处理器-D标志值?

来自分类Dev

如何自动将预处理器放入C

来自分类Dev

如何从预处理器启用内部函数

来自分类Dev

如何通过Inno Setup在预处理器函数中使用预处理器变量

来自分类Dev

如何通过MinGW Make定义预处理器宏

来自分类Dev

如何测试C预处理器-D标志值?

来自分类Dev

在以下示例中,预处理器如何工作?

来自分类Dev

我如何在运行时取消定义和定义预处理器的值

来自分类Dev

使用Pixate Freestyle iOS运行预处理器

来自分类Dev

CSS预处理器?

来自分类Dev

如何使用boost预处理器生成访问器?

来自分类Dev

如何使用html2js预处理器?

来自分类Dev

C ++预处理器

来自分类Dev

如何在gcc程序集中启用预处理器

来自分类Dev

如何在预处理器中检测-stdlib = libc ++?

来自分类Dev

在Rails中编译资产文件时,预处理器引擎如何运行?

来自分类Dev

如何欺骗预处理器