What Is "Pch.H" and Why Is It Needed to Be Included as the First Header File

What is pch.h and why is it needed to be included as the first header file?

pch stands for precompiled header.

In computer programming, a precompiled header is a (C or C++) header file that is compiled into an intermediate form that is faster to process for the compiler. Usage of precompiled headers may significantly reduce compilation time, especially when applied to large header files, header files that include many other header files, or header files that are included in many translation units.

To reduce compilation times, some compilers allow header files to be compiled into a form that is faster for the compiler to process. This intermediate form is known as a precompiled header, and is commonly held in a file named with the extension .pch or similar, such as .gch under the GNU Compiler Collection.

In Visual Studio, precompiled header is usually named "pch.h" (for console based applications), but it is possible to use different name, or not use it at all. Which file would be precompiled header, if any, is determined by projects settings.

If the precompiled header file is "pch.h" and the compile option is /Yu, Visual Studio will not compile anything before the #include "pch.h" in the source file; it assumes all code in the source up to and including that line is already compiled.

How precompiled headers actually works

I have no particular knowledge of VC++'s innards. 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. 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. 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. 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. Other restrictions on the contents of precompiled headers could also result in some additional functionality being shifted into the precompilation phase.

Getting started with PCH in a VC++ project

After configuring my project to use PCH, full-compile times were down to half, and incremental builds occurred almost instantly. PCH is a very effective way to speedup compile times, and I highly recommend it.

Althouh dsharlet mentions many important points, he skips some crucial steps that I had to eventually figure out. So here is the complete guide to configuring your project to use PCH:

Getting started with PCH in a VC++ project

  1. Backup your src dir and any other directories that contain source code ... (you'll need this in case anything goes wrong)

  2. Create 2 files in your project, Globals.cpp and Globals.h .. (choose any name but stick to it)

  3. Right click Globals.cpp and open Properties, choose Configuration > All configurations

  4. Go to C/C++ | Precompiled Header, and fill these in:

    • Precompiled Header : Create (/Yc)
    • Precompiled Header File : Globals.h
  5. Open Globals.cpp and add this one line in, and nothing more: #include "Globals.h"

  6. Right click your VC++ project and open Properties, choose Configuration > All configurations

  7. Go to C/C++ | Precompiled Header, and fill these in:

    • Precompiled Header : Use (/Yu)
    • Precompiled Header File : Globals.h
  8. Open all the .h and .cpp files in your project, and add this at the very top: #include "Globals.h". If you DONOT want to include every file manually, you can use the Force Include /FI[name].

  9. Open Globals.h and add the following in: (its very important you have #pragma once at the top)

      #pragma once

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdarg.h>
    #include <stddef.h>
    #include <memory>
    #include <string.h>
    #include <limits.h>
    #include <float.h>
    #include <time.h>
    #include <ctype.h>
    #include <wchar.h>
    #include <wctype.h>
    #include <malloc.h>
    #include <locale.h>
    #include <math.h>

    // Windows SDK
    #define _WIN32_WINNT 0x0501 // _WIN32_WINNT_WINXP
    #include <SDKDDKVer.h>

    // Windows API
    #define WIN32_LEAN_AND_MEAN
    #include <windows.h>
  • These includes are typical candidates for your PCH file
  • Remove the includes that you're not using
  • Go through your project and collect any more header files that do not change often

  1. Using find and replace, search for each of the #include's in your PCH file, and remove them from all the .h and .cpp files in your project.

  2. Do a full compile and ensure everything is working okay. Here are some solutions for common problems you'll encounter:


PCH file includes itself:

Your PCH file is including a header that includes the PCH header file again, creating a kind of circular dependency. Double click the error to take you to the offending file, and simply remove the line that says #include "Globals.h"


Undefined symbol X

Although all your project files can include the PCH header, the files included inside the PCH header cannot include the PCH header! (as stated above) so you'll need to add back any imports that were previously in the file. (diff the file with the backup version)


Cannot find symbol logf

Sometimes the global PCH file does not behave as expected, and breaks compiling with crazy errors that are impossible to solve. You can then turn off PCH for individual source code files.

  1. Right click your .cpp file and open Properties, choose Configuration > All configurations

  2. Go to C/C++ | Precompiled Header, and fill these in:

    • Precompiled Header : Not Using Precompiled Headers
  3. Remove the line #include "Globals.h" in your .cpp file

  4. Add back whatever imports the file originally had. (diff the file with the backup version)

Why do I need to include the precompiled header in all the files in C++?

If you use them, then you must include them. But you can turn them off in the project properties.

However, the recommended way to use them is to "Force Include" the PCH from the command line, so that the file itself doesn't contain the PCH. That way the source file can be compatible with other systems.

Comprehension issue: Precompiled headers & include usage

This will be a bit of a blanket statement with intent. The typical setup for PCH in a Visual Studio project follows this general design, and is worth reviewing. That said:

  1. Design your header files as if there is no PCH master-header.
  2. Never build include-order dependencies in your headers that you expect the including source files will fulfill prior to your headers.
  3. The PCH master-header notwithstanding (I'll get to that in a moment), always include your custom headers before standard headers in your source files. This makes your custom header is more likely to be properly defined and not reliant on the including source file's previous inclusion of some standard header file.
  4. Always set up appropriate include guards or pragmas to avoid multiple inclusion. They're critical for this to work correctly.

The PCH master-header is not to be included in your header files. When designing your headers, do so with the intent that everything needed (and only that which is needed) by the header to compile is included. If an including source file needs additional includes for its implementation, it can pull them in as needed after your header.

The following is an example of how I would setup a project that uses multiple standard headers in both the .h and .cpp files.

myobject.h

#ifndef MYAPP_MYOBJECT_H
#define MYAPP_MYOBJECT_H

// we're using std::map and std::string here, so..
#include <map>
#include <string>

class MyObject
{
// some code

private:
std::map<std::string, unsigned int> mymap;
};

#endif

Note the above header should compile in whatever .cpp it is included, with or without PCH being used. On to the source file...

myobject.cpp

// apart from myobject.h, we also need some other standard stuff...

#include "myobject.h"
#include <iostream>
#include <fstream>
#include <algorithm>
#include <numeric>

// code, etc...

Note myobject.h does not expect you to include something it relies on. It isn't using <iostream> or <algorithm>, etc. in the header; we're using it here.

That is a typical setup with no PCH. Now we add the PCH master


Adding the PCH Master Header

So how do we set up the PCH master-header to turbo-charge this thing? For the sake of this answer, I'm only dealing with pulling in standard headers and 3rd-party library headers that will not undergo change with the project development. You're not going to be editing <map> or <iostream> (and if you are, get your head examined). Anyway...

  1. See this answer for how a PCH is typically configured in Visual Studio. It shows how one file (usually stdafx.cpp) is responsible for generating the PCH, the rest of your source files then use said-PCH by including stdafx.h.

  2. Decide what goes in the PCH. As a general rule, that is how your PCH should be configured. Put non-volatile stuff in there, and leave the rest for the regular source includes. We're using a number of system headers, and those are going to be our choices for our PCH master.

  3. Ensure each source file participating in the PCH turbo-mode is including the PCH master-header first, as described in the linked answer from (1).

So, first, the PCH master header:

stdafx.h

#ifndef MYAPP_STDAFX_H
#define MYAPP_STDAFX_H

// MS has other stuff here. keep what is needed

#include <algorithm>
#include <numeric>
#include <iostream>
#include <fstream>
#include <map>
#include <string>

#endif

Finally, the source files configured to use this then do this. The minimal change needed is:

UPDATED: myobject.cpp

#include "stdafx.h" // <=== only addition
#include "myobject.h"
#include <iostream>
#include <fstream>
#include <algorithm>
#include <numeric>

// code, etc...

Note I said minimal. In reality, none of those standard headers need appear in the .cpp anymore, as the PCH master is pulling them in. In other words, you can do this:

UPDATED: myobject.cpp

#include "stdafx.h"
#include "myobject.h"

// code, etc...

Whether you choose to or not is up to you. I prefer to keep them. Yes, it can lengthen the preprocessor phase for the source file as it pulls in the headers, runs into the include-guards, and throws everything away until the final #endif. If your platform supports #pragma once (and VS does) that becomes a near no-op.

But make no mistake: The most important part of all of this is the header myobject.h was not changed at all, and does not include, or know about, the PCH master header. It shouldn't have to, and should not be built so it has to.



Related Topics



Leave a reply



Submit