GCC和预编译头

在阅读了这篇精美的文章 (“编译头文件的关心和喂养”)之后,我对这些文章在现实生活中如何工作有些怀疑。 更具体地说,我怎么能知道我需要在以下情况下触发重新编译的头文件:

  • 我决定在我的一个.cpp文件中定义一些东西,它改变了预处理器解释已经包含在我的预编译头文件中的一些头文件
  • 我在其中一个.cpp文件中包含另一个头文件,它定义了一个特定的预处理器指令,用于改变预处理器解释已包含在预编译头文件中的头文件的方式
  • 更糟糕的是,以前的问题可以recursion地发生,当某些头文件#include其他头文件

预编译的头文件的使用是否强制执行某种限制性的编码风格,如将.cpp文件中包含的头文件的数量限制为1,而不是在.cpp文件中定义文件?

虽然微软的编译器可能在预编译的头文件(通过应用一些特定于MS的巫术工具)上做了一个体面的工作,因为据我所知,它提供了应该做所有pipe道的/Yc/Yu选项,对于GCC看起来这个function在Makefile中需要大量的手工工作和创造性,而且我无法find一个模板来解决所有使用预编译头文件的缺陷。

例如,如果我有一个build立几个库的项目,为了在每次更改之后不重build所有的库,我必须在Makefile中使用一些非常可爱的sed技巧来检测是否有一个标头#include d库被修改了(或者#include一个修改的头文件)。 我害怕甚至想到预构build头文件实际上意味着为了使构build脚本在每次必要时重build它们的复杂性。

当前的GCC(即4.7)及其以前的版本,只有当你的应用程序有一个单独的公共头时,它才能和预编译的头一起工作,当这个头(包括所有的系统头文件和特定的头文件)由应用程序)是#include -d(作为您的源的第一个非注释的词义 )由您的应用程序的每个来源。

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

回想一下, -H是一个有用的选项,让gcc告诉你什么是包含的。

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

GCC包含了一个预编译的头文件后,当然可以使用#definemacros, #include一些非预编译头文件,用#ifdef等进行条件编译。但是预处理将不会被“预编译” !

这可能不适合你的需求或习惯。

有些人(特别是Google,特别是Diego Novillo)正在研究PreParsed Header(pph)分支来改善这种情况,但目前的GCC主干还没有完成这项工作。

关于GCC的行为的解释是,预处理的头本质上是整个GCC堆的持久序列化检查点(与GCC中的内存pipe理有关,通过Ggc和GTY以及gengtype )。 只有当gcc处于其初始空状态时,才能加载该检查点堆。 一旦gcc (实际上是cc1或者cc1plus )知道它已经不能加载任何预编译头文件*.h.gch并且将恢复到parsing文本头文件*.h


附录(2014年11月)

甚至GCC 4.9也希望有一个预编译的头文件。 迭戈·诺维洛(Diego Novillo)等人预先分析的头球的努力。 已被放弃。

C ++标准的未来版本( C ++ 14之后 )可能会定义一个模块机制。 参见例如n4047build议。

(附加增编,2015年12月)这对GCC-5和未来的GCC-6仍然适用。