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

2024-03-06

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

#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, where -E预处理后停止,并且-P排除线标记的生成。
我似乎找不到排除系统标头处理的标志。


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

Setup

Files:

./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,就是你想要的。

Analysis

The -Dinclude=#include参数相当于#define include #include。当预处理器从宏生成输出时,即使它看起来像指令(例如#include),它不是预处理器指令。引用 C++11 标准 ISO/IEC 14882:2011(据我所知,这并不是说版本之间发生了变化,而且逐字地与 C11 标准、ISO/IEC 9899:2011 中的内容相同,在 §6.10.3 中) :

§16.3 宏替换

¶8 If a #预处理标记,后跟标识符,在词法上出现在预处理指令可以开始的位置,标识符不受宏替换的影响。

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

如果您不喜欢使用宏名称include,你可以将其更改为任何适合你的其他内容 -syzygy, apoplexy, nadir, reinclude,... - 并更改代理标头以使用该名称,并在预处理器(编译器)命令行上定义该名称。优点之一include是你不可能有任何东西使用它作为宏名称。

自动生成代理标头

osgx https://stackoverflow.com/users/196561/osgx asks https://stackoverflow.com/questions/20889460/how-do-i-run-the-preprocessor-on-local-headers-only#comment63755844_38163793:

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

有多种选择。一种是分析您的代码(使用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

添加了一些格式后,当我用我对以下问题的答案扫描源代码树时,会生成这样的标题列表:

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将接受指定代理标头目录的参数。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何仅在本地标头上运行预处理器? 的相关文章

  • “构建”构建我的项目,“构建解决方案”则不构建

    我刚刚开始使用VS2010 我有一个较大的解决方案 已从 VS2008 成功迁移 我已将一个名为 Test 的控制台应用程序项目添加到解决方案中 选择构建 gt 构建解决方案不编译新项目 选择构建 gt 构建测试确实构建了项目 在失败的情况
  • 按成员序列化

    我已经实现了template
  • 在结构中使用 typedef 枚举并避免类型混合警告

    我正在使用 C99 我的编译器是 IAR Embedded workbench 但我认为这个问题对于其他一些编译器也有效 我有一个 typedef 枚举 其中包含一些项目 并且我向该新类型的结构添加了一个元素 typedef enum fo
  • Clang 3.1 + libc++ 编译错误

    我已经构建并安装了 在前缀下 alt LLVM Clang trunk 2012 年 4 月 23 日 在 Ubuntu 12 04 上成功使用 GCC 4 6 然后使用此 Clang 构建的 libc 当我想使用它时我必须同时提供 lc
  • 如何从 appsettings.json 文件中的对象数组读取值

    我的 appsettings json 文件 StudentBirthdays Anne 01 11 2000 Peter 29 07 2001 Jane 15 10 2001 John Not Mentioned 我有一个单独的配置类 p
  • C# 中通过 Process.Kill() 终止的进程的退出代码

    如果在我的 C 应用程序中 我正在创建一个可以正常终止或开始行为异常的子进程 在这种情况下 我通过调用 Process Kill 来终止它 但是 我想知道该进程是否已退出通常情况下 我知道我可以获得终止进程的错误代码 但是正常的退出代码是什
  • 带动态元素的 WPF 启动屏幕。如何?

    我是 WPF 新手 我需要一些帮助 我有一个加载缓慢的 WPF 应用程序 因此我显示启动屏幕作为权宜之计 但是 我希望能够在每次运行时更改屏幕 并在文本区域中显示不同的引言 这是一个生产力应用程序 所以我将使用非愚蠢但激励性的引言 当然 如
  • 重载<<的返回值

    include
  • 如何在整个 ASP .NET MVC 应用程序中需要授权

    我创建的应用程序中 除了启用登录的操作之外的每个操作都应该超出未登录用户的限制 我应该添加 Authorize 每个班级标题前的注释 像这儿 namespace WebApplication2 Controllers Authorize p
  • 什么时候虚拟继承是一个好的设计? [复制]

    这个问题在这里已经有答案了 EDIT3 请务必在回答之前清楚地了解我要问的内容 有 EDIT2 和很多评论 有 或曾经 有很多答案清楚地表明了对问题的误解 我知道这也是我的错 对此感到抱歉 嗨 我查看了有关虚拟继承的问题 class B p
  • 这些作业之间是否存在顺序点?

    以下代码中的两个赋值之间是否存在序列点 f f x 1 1 x 2 不 没有 在这种情况下 标准确实是含糊不清的 如果你想确认这一点 gcc 有这个非常酷的选项 Wsequence point在这种情况下 它会警告您该操作可能未定义
  • GCC 如何运行其他程序?

    也许标题并没有那么准确地表达问题 我知道当我跑步时gcc foo cGCC 调用其他为其完成所有工作的子程序 使 gcc 主程序只是一个接口 但这到底是如何完成的呢 是否使用system or exec或者其他一些功能 我之所以想知道这个是
  • 链接器错误:已定义

    我尝试在 Microsoft Visual Studio 2012 中编译我的 Visual C 项目 使用 MFC 但出现以下错误 error LNK2005 void cdecl operator new unsigned int 2
  • 如何使用 C# / .Net 将文件列表从 AWS S3 下载到我的设备?

    我希望下载存储在 S3 中的多个图像 但目前如果我只能下载一个就足够了 我有对象路径的信息 当我运行以下代码时 出现此错误 遇到错误 消息 读取对象时 访问被拒绝 我首先做一个亚马逊S3客户端基于我的密钥和访问配置的对象连接到服务器 然后创
  • 为什么编译时浮点计算可能不会得到与运行时计算相同的结果?

    In the speaker mentioned Compile time floating point calculations might not have the same results as runtime calculation
  • 如何在Xamarin中删除ViewTreeObserver?

    假设我需要获取并设置视图的高度 在 Android 中 众所周知 只有在绘制视图之后才能获取视图高度 如果您使用 Java 有很多答案 最著名的方法之一如下 取自这个答案 https stackoverflow com a 24035591
  • 将控制台重定向到 .NET 程序中的字符串

    如何重定向写入控制台的任何内容以写入字符串 对于您自己的流程 Console SetOut http msdn microsoft com en us library system console setout aspx并将其重定向到构建在
  • 如何将服务器服务连接到 Dynamics Online

    我正在修改内部管理应用程序以连接到我们的在线托管 Dynamics 2016 实例 根据一些在线教程 我一直在使用OrganizationServiceProxy out of Microsoft Xrm Sdk Client来自 SDK
  • 如何在文本框中插入图像

    有没有办法在文本框中插入图像 我正在开发一个聊天应用程序 我想用图标图像更改值 等 但我找不到如何在文本框中插入图像 Thanks 如果您使用 RichTextBox 进行聊天 请查看Paste http msdn microsoft co
  • 使用.NET技术录制屏幕视频[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 有没有一种方法可以使用 NET 技术来录制屏幕 无论是桌面还是窗口 我的目标是免费的 我喜欢小型 低

随机推荐

  • Git 使用错误的子密钥使用 GPG 密钥对提交进行签名

    我对 git 和使用 GPG 密钥签署提交有疑问 我有一个主密钥 仅用于认证 我为每台计算机创建了两个子密钥 仅用于签名 我已将公钥添加到 Github 并使用配置了 gituser signingKey配置选项 问题是 git 似乎忽略了
  • 在 Android 中抑制包中的 toast

    我正在开发一个 Android 应用程序 我试图用Toast 我用了Toast在其他项目中一切正常 但在这个应用程序中 当Toast应该出现 但它没有出现 Logcat 显示下一条消息 Suppressing toast from pack
  • 通过ajax打开FileStreamResult(作为下载的文件)

    是否可以使用 ajax 调用将 FileStreamResult 作为下载的文件打开 控制器方式 public FileStreamResult DownloadPDF var stream myHandler getFileStream
  • 在 iOS 上哪里可以添加来自 Firebase 的数据库引用?

    我正在按照此说明在 iOS 应用程序上安装和设置实时数据库link https firebase google com docs database ios start set up firebase database我很困惑在哪里添加声明s
  • Solr 查询无法正常工作

    我不知道出了什么问题 这实际上是一个非常简单的查询 在我的 Solr 搜索中不起作用 http IP ADDRESS solr CORE NAME select indent on q Bangalore wt json 我的数据库中有超过
  • SqlCommand-SqlConnection使用处置问题

    根据 MSDN 如果 IDisposable 资源 嵌套的内部using语句包含外部using的资源 语句中 嵌套资源的 Dispose 方法释放 包含的资源 MSDN http msdn microsoft com en us libra
  • 在ContinueWith()之后,ConfigureAwait(False)不会改变上下文

    我不知道我是否做错了什么 或者我在异步库中发现了错误 但在我使用 continueWith 返回到同步上下文后运行一些异步代码时 我发现了一个问题 更新 代码现在运行 using System using System ComponentM
  • 如果出现新组合,VBA 在表中添加行

    我有点难以找到一种根据资本支出类别 合作伙伴数量和期间向表中可变地添加行的方法 Mu final table looks like more sections but for example it is not a listobject i
  • 指定 Maven security-settings.xml 文件的自定义位置?

    使用 Maven 您可以轻松指定 settings xml 位置 例如 mvn s custom dir settings xml package 有没有类似的方法来指定自定义 security settings xml 其背后的原因很简单
  • 使用日历检查日期是否有效

    我想使用日历方法来设置年份和月份 但想要某种指示如果日期无效 例如 calendar set 2013 Calendar JANUARY 23 is a valid date calendar set 2013 Calendar JANUA
  • 如何启用/禁用 JEditable

    SOLUTION 感谢 Arman 的 P 概念验证 终于让它可以与我的网站一起使用了 CODE Edit Note function function makeEditable edit editable ajax save php ed
  • CStr() 与 Str() 与 .ToString()

    我想知道两者之间到底有什么区别CStr Str and ToString Label1 Text CStr Int Rnd 10 and Label1 Text Str Int Rnd 10 and Label1 Text Int Rnd
  • 以编程方式将主题应用到按钮

    是否可以以编程方式将 Widget AppCompat Button 主题应用于按钮 Button button new Button context button setText Button 目前 我正在使用自定义可绘制资源 尝试实现类
  • 如何从GridView中删除一行?

    我在用GridView控制在asp net questions tagged asp net 2005 c questions tagged c 23 using 如何从中删除特定行GridView 我编写了以下代码 但这不起作用 Data
  • 退出应用程序会导致错误“来自调试器的消息:由于信号 9 而终止”

    我正在编写一个基本的音乐播放器应用程序 但在处理应用程序状态转换时遇到一些问题 我正在使用 Swift 3 和 MPMusicPlayerController systemMusicPlayer 目标是这样的 1 当用户点击主页按钮并且应用
  • 使用 Sass 以可重用的方式设计一组特定的输入类型

    我想要一个 mixin 函数 它返回 HTML5 输入类型的列表 我想在一个地方管理它 当新类型出现时 改变函数 而不是代码中其他地方的所有地方 问题似乎是 mixins 的设计目的不是返回可以在 CSS 花括号之外使用的字符串 这是我的
  • jQuery 热键 - 解除绑定?

    我有一个 jQuery 对话框 它初始化热键如下 循环从 1 到 9 问题是 如果您关闭对话框然后重新打开对话框 它不断重新绑定 因此当您按下 1 键时 它会运行两次 三次 四次等 它会不断增长 我尝试杀死对话框上的键绑定关闭 docume
  • 带参数和选项的 C# 方法重载

    今天我发现了一些奇怪的事情 我想知道为什么这有效 static void Main string args Console WriteLine ExampleMethod 3 Console ReadKey public static st
  • 无反向匹配 /

    我正在尝试制作非常有意义的网址 但我想我做错了 这有效 from django conf urls defaults import patterns url from places views import explore view url
  • 如何仅在本地标头上运行预处理器?

    我希望预处理器读取本地标头的包含内容 但忽略系统标头的包含内容 换句话说 如何让预处理器跳过以下形式的预处理指令 include