4

I understand the idea that precompiling headers can speed up build times, but there are a handful of questions that have thus far prevented me from grokking them.

  1. Why does using precompiled headers require the developer to configure anything?

    Why can't the compiler (or linker/IDE?) just have individual precompiled header object files, in the same way it does for the source files (i.e. .obj files)? Dependencies are indicated by which source/header files include which other files, and it can already detect when source files change, so a regular build is normally not a full rebuild. Instead of requiring me to specify which headers get precompiled, etc., why isn't this all just always automatically on, and transparent to the developer?

  2. As I understand the precompiled headers methodology in Visual Studio, the idea is that you get this one big header file (stdafx.h) that includes all the other header files that you want to be precompiled, and then you include that in all your source files that use any of those headers.

    a. Am I understanding correctly?

    b. Doesn't this break encapsulation? Often effectively including various (likely) unrelated items that you don't, which makes it harder to tell what libraries you're actually using, and what comes from where.

    It seems to me that this implementation forces bad practices. What am I missing?

  3. How do I utilize precompiled headers in Visual Studio (2013)?

  4. Is there a cross-platform way to use or facilitate precompiled headers?

Thanks.

zerocukor287
  • 555
  • 2
  • 8
  • 23
cp.engr
  • 2,291
  • 4
  • 28
  • 42
  • 1
    (1) Source code can change depending on the order of the included files. Think about the order of #define(s) and/or #ifdef(s) for example. – Richard Critten May 13 '15 at 21:20
  • Each one of these questions, alone, would be a good question here. Some or all might be duplicates. I think you're limiting the answers you get by asking for "an answer" to a large bundle of questions. I predict mostly comments here choosing one question to answer. – Drew Dormann May 13 '15 at 21:21
  • From what you've shown, most of your confusion regarding how VS pch works is covered by the [documention by MS](https://msdn.microsoft.com/en-us/library/szfdksca.aspx). The fundamental purpose of PCH is to precompile that which is used by your project **and** has very low or ideally **no** volatility. Project-defined headers generally do *not* fit into that category, while runtime library headers, 3rd-party headers, etc., generally *do*. – WhozCraig May 13 '15 at 21:22
  • I usually only include external header files for precompiling. System header files, header files for prebuilt libraries etc. – Richard Critten May 13 '15 at 21:24
  • (4) It's compiler specific. But can be cross-platform with a cross-platform compiler. – Richard Critten May 13 '15 at 21:28

2 Answers2

0

Why can't the compiler (or linker/IDE?) just have individual precompiled header object files, in the same way it does for the source files (i.e. .obj files)?

The answer to 1 and 2 is in the way how precompiled headers work. Assume you have a my_source_file.c:

#include "header1.h"
#include "header2.h"

int func(int x) { return x+1; }

my_other_source_file.c:

#include "header1.h"

int func2(int x) { return x-1; } 

When you call compiler.exe my_source_file.c the compiler starts parsing your file. All the internal variables of the compiler (things like which types have been defined, what variables declared, etc) are called the compiler state.

After it has parsed header1.h it can save the state to the disk. Then, when compiling my_other_source_file.c, instead of parsing header1.h again, it can just load the state and continue.

That state is a precompiled header. It is literally just a dump of all the compiler variables in the moment after it has parsed the entire header.

Now, the question is why can't you have two state dumps, for header1.h and header2.h and just load them both.. Well, the states are not independent. The second file would be the state of header1.h + header2.h. So, what is usually done is you have one state which is after all the common header files have been compiled, and use that.

In theory, you could have one for every combination and use the appropriate one, but that is much more hassle than it's worth.

Some things that are side effects of how this is done:

  • Different compilers (including even minor versions) have different variables, so you can't reuse the precomps.

  • Since the dumped state started from the top of the file, your precomp must be the first include. There must be nothing that could influence the state (i.e. not #defines, typedefs, declarations) before including the precomp.

  • Any defines passed by the command line (-DMY_DEFINE=0) will not be picked up in the precompiled header.

  • Any defines passed by the command line while precompiling will be in effect for all source files that use the precomp.

For 3), refer to MSFT documentation.

For 4), most compilers support precompiled headers, and they generally work in the same way. You could configure your makefiles/build scripts to always precompile a certain header (e.g. stdafx.h) which would include all the other headers. As far as your source code goes, you'd always just #include "stdafx.h", regardless of the platform.

mtijanic
  • 2,872
  • 11
  • 26
  • So on my Q 2, am I understanding you correctly that the answer is basically yes, it does violate preferred encapsulation practices, due to the implementation / limitations of C/C++? i.e. Including header1.h and header2.h in the precompiled header in your example would result in both of those being included in both source files, even though it's only needed in the one. – cp.engr May 13 '15 at 22:11
  • 1
    @cp.engr Yes, that is correct, you will pretty much always have extra stuff included that you do not need. Usually, this doesn't matter though.. For example, if you precompiled all of the C standard library headers, or even all of the `std` namespace, it wouldn't be a problem. On large projects, you likely have the files grouped somehow, for example "encryption" and "image manipulation", and you can have a special precomp for each, if you don't want the headers mixing in. – mtijanic May 14 '15 at 08:31
0

Why can't the compiler (or linker/IDE?) just have individual precompiled header object files

C and C++ have no concept of modules. The traditional compiler has a preprocessor phase (which may be invoked as a separate program) that will include the files and the whole thing will get compiled to intermediate code. The compiler per se does not see includes (or comments, or trigraphs, etc.).

Add to this that the behaviour of a header file can change depending on the context in which it is included (think macros, for example) and you end up with either many precompiled versions of the same header, or an intermediate form that is basically the language itself.

Am I understanding correctly?

Mostly. The actual name is irrelevant, as it can be specified in the project options. stdafx.h is a relic of the early development of MFC, which was originally named AFX (Application Framework eXtensions). The preprocessor also treats includes of the precompiled header differently, as they are not looked up in the include paths. If the name matches what is in the project settings, the .pch is used automatically.

Doesn't this break encapsulation

Not really. Encapsulation is an object-oriented feature and has nothing to do with include files. It might increase coupling and dependencies by making some names available across all files, but in general, this is not a problem. Most includes in a precompiled header are standard headers or third-party libraries, that is, headers that may be large and fairly static.

As an example, a project I'm currently working on includes GTK, standard headers, boost and various internal libraries. It can be assumed that these headers never change. Even if they changed once a day, I probably compile every minute or so on average, so it is more than worth it.

The fact that all these names are available project-wide makes no difference. What would I gain by including boost/tokenizer.hpp in only one .cpp file? Perhaps some intellectual satisfaction of knowing that I can only use boost::char_separator in that particular file. But it certainly creates no problem. All these headers are part of a collection of utilities that my program can use. I am completely dependent on them, because I made a design decision early on to integrate them. I am tightly coupled with them by choice.

However, this program needs to access system-specific graphical facilities, and it needs to be portable on (at least) Debian and Windows. Therefore, I centralized all these operations in two files: windows.cpp and x11.cpp. They both include their own X11/Xlib.h and windows.h. This makes sure I don't use non-portable stuff elsewhere (which would however quickly be caught as I keep switching back and forth) and it satisfies my obsession with design. In reality, they could have been in the precompiled header. It doesn't make much of a difference.

Finally, none of the headers that are part of this specific program are in the precompiled header. This is where coupling and dependencies come into play. Reducing the number of available names forces you to think about design and architecture. If you try to use something and get an error saying that that name isn't declared, you don't blindly include the file. You stop and think: does it make sense for this name to be available here, or am I mixing up my user interface and data acquisition? It helps you separate the various parts of your program.

It also serves as a "compilation firewall", where modifying a header won't require you to rebuild the whole thing. This is more of a language issue than anything else, but in practice, it's still damn useful.

Trying to localize the GTK includes, for example, would not be helpful: all of my user interface uses it. I have no intention of supporting a different kind of toolkit. Indeed, I chose GTK because it was portable and I wouldn't have to port the interface myself.

What would be the point of only including the GTK headers in the user interface files? Obviously, it will prevent me from using GTK in files where I don't need to. But this is not solving any problem. I'm not inadvertently using GTK in places I shouldn't. It only slows down my build time.

How do I utilize precompiled headers in Visual Studio

This has been answered elsewhere. If you need more help, I suggest you ask a new question, as this one is already pretty big.

Is there a cross-platform way to use or facilitate precompiled headers?

A precompiled header is a feature provided by your compiler or build system. It is not inherently tied to a platform. If you are asking whether there is a portable way of using precompiled headers across compilers, then no. They are highly compiler-dependent.

Community
  • 1
  • 1
isanae
  • 3,253
  • 1
  • 22
  • 47
  • "It might increase coupling and dependencies by making some names available across all files, but in general, this is not a problem." Maybe 'breaking encapsulation' wasn't optimal phrasing, but my point is that doing this exposes a whole bunch of stuff that YAGN[I]. Personally, I think it obfuscates where things are coming from, and makes it harder to keep a module tightly focused. ("Module" not in the language-specific sense; often consisting of a .c and a .h file.) Else, e.g. why not have one include file for the whole STL? – cp.engr May 14 '15 at 00:35
  • I'd personally welcome an "always available" standard library, as long as it doesn't impact build time. Including 3rd-party headers in a pch might sound bad, but in general, it makes no difference. Coupling is important _within_ your program, not with 3rd-party. – isanae May 14 '15 at 03:40
  • You're not addressing my points. You're saying it doesn't matter, but not explaining why. "Coupling is important within your program, not with 3rd-party." How did you make that determination? On what basis do you differentiate? – cp.engr May 14 '15 at 04:00
  • Based on my experience and knowledge. How else? And it is hard to be verbose in comments. SO is not suited for chatting back in forth, so I'll edit my post in a few minutes with more information. – isanae May 14 '15 at 04:07
  • @cp.engr I just added a rather long explanation. I hope you find it interesting. – isanae May 14 '15 at 04:40