简体   繁体   English

预编译头文件的实际工作原理

[英]How precompiled headers actually works

All my quetions are related to vc++ compiler but I guess other c++ compilers have the same behavior.我所有的问题都与 vc++ 编译器有关,但我猜其他 c++ 编译器具有相同的行为。

  1. Are the precompiled headers a preprocessor-related stuff or this is all about the compilation process?预编译头文件是与预处理器相关的内容还是与编译过程有关? Or both?还是两者兼而有之? I have several guess:我有几个猜测:
    • PCH-engine only expands MACRO-definitions and nested headers and translates them into binary format (pch file). PCH 引擎只扩展 MACRO 定义和嵌套头并将它们转换为二进制格式(pch 文件)。 In this case all source-files (I mean cpp/hpp which may be included in PCH too) will being recompiled in EVERY source files in project.在这种情况下,所有源文件(我的意思是 cpp/hpp 也可能包含在 PCH 中)将在项目中的每个源文件中重新编译。 Or not?还是不行?
    • All source-files will be compiled only once and pulled into single obj-file?所有源文件将只编译一次并拉入单个 obj 文件? For example, how many times will be compiled variant library in this example?例如,在这个例子中,variant library 会被编译多少次? Ie only once - in PCH or two times - not in PCH but in both *.cpp files or three times - in PCH and both *.cpp files?即只有一次 - 在 PCH 或两次 - 不是在 PCH 但在两个 *.cpp 文件中或三次 - 在 PCH 和两个 *.cpp 文件中? and why?为什么?

//stdafx.h
#include <boost/variant/variant.hpp>

//test1.cpp
#include "stdafx.h"
#include <boost/variant/variant.hpp>
...

//test2.cpp
#include "stdafx.h"
...
  1. What files should I put in precompiled headers?我应该在预编译头文件中放入哪些文件? I guess this is something that used everywhere in project and changed very rarely.我想这是在项目中随处使用并且很少改变的东西。 What about libraries, boost for example?图书馆怎么样,例如提升? We use boost only in few source-files, should we put it in PCH?我们只在少数源文件中使用 boost,我们应该把它放在 PCH 中吗?

I have no particular knowledge of VC++'s innards.我对 VC++ 的内脏没有特别的了解。 However, having some knowledge of compiler design and theory, what these so-called "precompiled headers" are can't be anything more than just the result of the initial lexical analysis and tokenization phases of a classical compiler design.然而,有了一些编译器设计和理论的知识,这些所谓的“预编译头文件”不仅仅是经典编译器设计的初始词法分析和标记化阶段的结果。

Consider a simple header file that contains the following:考虑一个包含以下内容的简单头文件:

#ifdef FOO
#define BAR 10
#else
#undef FOOBAR
class Foo {
public:
     void bar();
};
#include "foobar.h"
#endif

You have to understand that the effect of using a so-called "pre-compiled" header must be identical to using the header file, as is.您必须了解使用所谓的“预编译”头文件的效果必须与使用头文件的效果相同。

Here, you don't really know what this header file is going to do.在这里,你真的不知道这个头文件要做什么。 It all depends on what preprocessor macros are defined when the header file is actually included.这一切都取决于实际包含头文件时定义的预处理器宏。 You don't know which macros this header file will define.你不知道这个头文件将定义哪些宏。 You don't know which macros this header file will undefine.你不知道这个头文件将取消定义哪些宏。 You don't know what other header files this header file will include.你不知道这个头文件会包含哪些其他头文件。 You don't really know a lot, here.你真的知道的不多,这里。

The only thing you can conceptually do, to "precompile" a header file, is to pre-parse it.从概念上讲,要“预编译”头文件,您唯一可以做的就是预解析它。 Convert the individual elements of the language, the individual keywords -- like "#ifdef", "class", and all others, into individual binary tokens.将语言的各个元素、各个关键字(如“#ifdef”、“class”和所有其他内容)转换为单独的二进制标记。 Remove any comments, whitespace, etc...删除任何评论,空格等...

The first phase of compiling a traditional language involves parsing the plain text source into the internal language elements.编译传统语言的第一阶段涉及将纯文本源解析为内部语言元素。 The lexical analysis and the tokenization phase.词法分析和标记化阶段。 After the individual language elements get parsed, then an attempt is made to figure out how the resulting, parsed source code, should get turned into an object module.在解析了各个语言元素之后,将尝试弄清楚如何将生成的解析源代码转换为对象模块。 And that's where 99% of the compiler's work is.这就是编译器 99% 的工作所在。 The initial lexical analysis phase is not really a lot, but that's pretty much all you can do to "precompile" the source code, and save the internal binary representation of the tokenized source, so that this phase can be skipped, when actual code that uses the "precompiled" source is compiled.最初的词法分析阶段并不是很多,但这几乎是“预编译”源代码并保存标记化源的内部二进制表示的全部内容,以便可以跳过此阶段,当实际代码为使用“预编译”源进行编译。

I am assuming that VC++ places little, if no restrictions at all, on the contents of precompiled headers.我假设 VC++ 对预编译头文件的内容几乎没有任何限制。 However, if there are some restrictions -- say, the precompiled headers cannot have any conditional preprocessor directives (ifdef/ifndef) except for the classical guards -- than it would be possible to do more work to produce the precompiled headers, and save a little bit more work, here.但是,如果有一些限制——比如,预编译头不能有任何条件预处理器指令(ifdef/ifndef),除了经典的守卫——那么就有可能做更多的工作来生成预编译头,并节省一个多一点工作,在这里。 Other restrictions on the contents of precompiled headers could also result in some additional functionality being shifted into the precompilation phase.对预编译头文件内容的其他限制也可能导致一些附加功能被转移到预编译阶段。

The precompiled header file, which gets compiled due to stdafx.cpp is stdafx.h .由于stdafx.cpp编译的预编译头文件是stdafx.h A developer would put rarely changed, and frequently needed header files and symbols in this header.开发人员会将很少更改且经常需要的头文件和符号放在此头文件中。 Such as Windows.h , vector and some global macros and symbols.Windows.hvector和一些全局宏和符号。 By frequently used, I mean across all files in given project.经常使用,我的意思是跨给定项目中的所有文件。

What's the purpose and helpfulness of such file (PCH)?此类文件 (PCH) 的目的和帮助是什么? Well, VC++ compiler will compile entire stdafx.h file, recursively, all headers included all macros and other symbols.好吧,VC++ 编译器将递归编译整个stdafx.h文件,所有头文件都包含所有宏和其他符号。 For first time, it will take a lot of time, and will produce a PCH file (hence p re- c ompiled h eader).对于第一次,这将需要大量的时间,并且将产生一个PCH文件(因此p重新ÇompiledħEADER)。 On subsequent builds, the elements included through stdafx.h will not be recompiled (as they are already in some binary/pre-compiled format).在后续构建中,通过stdafx.h包含的元素将不会被重新编译(因为它们已经是某种二进制/预编译格式)。 This reduces build time, and it would vary depending how many elements (headers, symbols etc.) are put through stdafx.h file.这减少了构建时间,并且它会根据通过stdafx.h文件放置的元素(标题、符号等)的数量而有所不同。

If you have a large codebase, and less of elements in stdafx, you wont get advantage (for example, including common Windows and STL headers everywhere, having externs and typedefs everywhere).如果您的代码库很大,并且 stdafx 中的元素较少,则您将无法获得优势(例如,到处都包含常见的 Windows 和 STL 标头,到处都有 extern 和 typedef)。 Better you find those elements, and put them into stdafx.h , and remove them from header/CPP files.最好找到这些元素,并将它们放入stdafx.h ,然后从头文件/CPP 文件中删除它们。 This will greatly reduce overall build times.这将大大减少整体构建时间。

This is where you can change it:这是您可以更改它的地方: 在此处输入图片说明

I think that MSVC looks for <application_name>.pch for the sole precompiled header for the translation unit and uses that instead of the transcluded header included under #line 1 "c:\\\\application_name\\\\stdafx.h" in the preprocessed .i file, if it is available.我认为 MSVC 寻找<application_name>.pch作为翻译单元的唯一预编译头,并使用它而不是预处理.i #line 1 "c:\\\\application_name\\\\stdafx.h"下包含的转置头文件(如果可用)。 The precompiled header is probably a serialised AST ie the header has been lexed and parsed into an AST representation.预编译头可能是序列化的 AST,即头已被词法分析并解析为 AST 表示。 It then does not need to lex (or parse) this region of the preprocessed output and just uses the .pch , which contains the lex+parse output of what is written in the preprocessor output under stdafx.h .然后它不需要 lex(或解析)预处理输出的这个区域,只使用.pch ,它包含在stdafx.h下预处理器输出中写入的内容的 lex+parse 输出。 The preprocessor has already done all other work on stafx.h , such as expanding macros (which don't appear in the .i file / preprocessor output).预处理器已经在stafx.h上完成了所有其他工作,例如扩展宏(不会出现在.i文件/预处理器输出中)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM