GCC 和预编译头文件

Posted

技术标签:

【中文标题】GCC 和预编译头文件【英文标题】:GCC and Precompiled Headers 【发布时间】:2012-09-08 09:52:58 【问题描述】:

阅读this nice article(预编译头文件的维护和提供)后,我对这些如何在现实生活中实际工作存在一些疑问。更具体地说,我怎么知道我需要在以下场景中触发预编译头的重建:

我决定 #define 在我的一个 .cpp 文件中更改预处理器解释某些已包含在我的预编译头文件中的头文件的方式 我在我的一个 .cpp 文件中包含另一个标头,#defines 是一个特定的预处理器指令,它改变了预处理器解释预编译标头中已包含的标头的方式 更糟糕的是,当某些标头 #include 其他标头时,上一个问题可能会递归发生

是否应该使用预编译的标头强制执行某种限制性编码样式,例如将 .cpp 文件中包含的标头数量限制为一个,而不是 #defineing .cpp 文件中的内容?

虽然微软的编译器可能在预编译头文件方面做得不错(通过应用一些特定于 MS 的巫术),因为据我所知,它提供了应该做所有事情的 /Yc/Yu 选项管道,对于 GCC,这个功能似乎需要在 Makefile 中进行大量的手动工作和创造性,我无法找到一个模板来解决使用预编译头文件的所有缺陷。

例如,如果我有一个构建多个库的项目,为了在每次更改后不重新构建所有库,我必须在 Makefile 中使用一些非常可爱的sed tricks 来检测其中一个标头 @987654330 @d 被当前库修改(或者它的#includesa 修改了头文件)。我什至害怕想到预先构建的标头实际上会带来的复杂性,以便构建脚本在每次有必要时都重新构建它们。

【问题讨论】:

【参考方案1】:

当前的 GCC(即 4.7)和它的早期版本只有在您的应用程序有一个 single 通用标头并且该单个标头(依次包括所有应用程序所需的系统和库特定的)是#include-d(作为您的源的第一个非注释词素)由您的应用程序的每个来源。

所以你应该有一个单个 yourapp.h 并且拥有yourapp 的每个源文件(即每个编译单元)开始#include "yourapp.h" 进行相同的预处理命令行上的选项(即-D-I-U)。 youapp.h 头文件通常是 #include-ing 许多其他文件,例如系统头文件(或 GTK 或 Qt 头文件)如 <stdlib.h><sys/poll.h> 或 [in C++] <algorithm><gtk/gtk.h><QtGui> 等。

回想一下,-H 是一个有用的选项,可以让 gcc 告诉您包含的内容。

如果需要,您的源文件可能会有一些额外的#include 之后 #include "yourapp.h"

在 GCC 包含一个 [single] 预编译头文件后,您当然可以#define 宏、#include 一些非预编译头文件、使用#ifdef 进行条件编译等。但预处理不会被“预编译”!

这可能不符合您的需求或习惯。

一些人(特别是来自 Google,特别是 Diego Novillo)正在努力通过 PreParsed Header (pph) branch 改善这种情况,但目前的 GCC 主干还没有完成这项工作。

关于 GCC 的这种行为的解释是,预处理标头本质上是整个 GCC 堆的持久序列化检查点(与 GCC 内的 memory management 到 Ggc 和 GTY 以及 gengtype 相关)。只有当gcc 处于其初始空状态时,才能加载该检查点堆。一旦gcc(实际上是cc1cc1plus)知道某些内容,它就不能再加载任何预编译的头文件*.h.gch,并将恢复解析文本头文件*.h


附录(2014 年 11 月及以后)

即使GCC 4.9 也需要一个预编译头文件。 Diego Novillo 等人的预解析标头工作。已经放弃了。

C++ 标准的未来版本(C++14)可能会定义模块机制。参见例如n4047 提案和C++20 标准。

(附加附录,2020 年夏季)这仍然适用于 GCC-10,其中存在多个 static analyzer 选项。另请参阅 Clang static analyzer 和 this draft 报告。考虑使用Frama-C。

【讨论】:

是的,一个包含所有内容的标题消除了大部分复杂性,但我已经看到了一个很好的例子,说明这会如何在我以前的工作场所搞砸。感谢您提供良好且有据可查的答案! 有趣。 Borland 引入了预编译头的概念,并且完全按照 PreParsed Headers 正在尝试做的事情做。然后微软出现并宣布他们的方法更好,每个人都效仿。 @Basile 关于您上次的编辑:是的,源可能有额外的#includes,但它们不能也是预编译标头的一部分,对吧? 单个预编译的头文件可能(并且通常应该)当然包含许多#include-s 只有一个yourapp.h文件可以作为预处理头文件,其yourapp.h.gch预处理形式仅在gcc遇到的第一个词位为#include "yourapp.h"时使用

以上是关于GCC 和预编译头文件的主要内容,如果未能解决你的问题,请参考以下文章

使用 GCC 预编译的 STL

gcc编译的过程

如何在不扩展包含的头文件的情况下预编译 C 源文件?

Part5 数据的共享与保护 5.6多文件结构和预编译命令

递归和预编译

编译过程学习