Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best practice for using precompiled headers in a modern C++Builder application?

I am currently migrating a large RAD Studio 2010 project to XE4. As part of this, I am recreating many of the project files. I would like to take the opportunity to ensure we are using the best possible mechanism for precompiled headers, since there seem to be a few ways to do it.

Right now we are compiling for 32-bit only but will use the 64-bit compiler in future.

Here's what we're currently doing in 2010, and why I'm unsure about what to do in XE4:

In RAD Studio 2010

We have a file PchApp.h which includes <vcl.h> and a number of other commonly-used header files, mostly headers for various commonly-used core classes in the project. This header is included at the top of every CPP file followed by #pragma hdrstop, like so:

// Top of .cpp file
#include "PchApp.h"
#pragma hdrstop

// Normal includes here
#include "other.h"
#include "other2.h"
// etc

We then have the following settings in the Precompiled Headers section of the project options:

Current RS2010 precompiled header settings

It is not particularly fast to compile (12 minutes for circa 350,000 lines of code.) I am unsure about:

  • "Inject precompiled header file": should this inject PchApp.h?
  • "Cache precompiled headers (Must be used with -H or -H"xxx")": the -H option is the "PCH filename", so we are using it, but surely the point of a precompiled header is that it is "cached" or prebuilt once per compile. What extra difference does this make?
  • Should we have the two lines to include PchApp.h and the pragma hdrstop in the .cpp files? Is there a way to do this in the project options only, and not duplicate these two lines in every single file? Are they necessary?

In other words, I am not sure these are correct or optimal settings, but from reading the documentation I'm equally not sure what would be better. I am aware I don't understand all the options well enough - one reason for this question :)

In RAD Studio XE4

The XE4 32-bit compiler's options dialog is the same, but two things confuse me and/or make me uncertain the current 2010 approach is the best.

1. Default behaviour

When creating a new VCL Forms project, the IDE creates a header named by default Project1PCH1.h, which is intended to be the project's precompiled header. This header includes <vcl.h> and <tchar.h>, and is shown as a node in the Project Manager. It is not included in the default Form1.cpp, but #include <vcl.h> followed by #pragma hdrstop is at the very top of Form1.cpp, followed by other headers.

The default XE4 settings dialog for a new project using this header is: XE4 default precompiled header settings

I am (naively?) working on the assumption the defaults are actually the best / most optimal settings. Some things puzzle me:

  • The project's supposed precompiled header Project1PCH1.h is not mentioned in the precompiled header settings anywhere.
  • The headers aren't cached
  • The PCH filename isn't specified (should this be Project1PCH1.h?)
  • The .cpp files don't include Project1PCH1.h either.

In fact I have no idea how the compiler or IDE actually know that it is supposed to use Project1PCH1.h or for which .cpp files it is supposed to use it, since it isn't referred to in any way I can find.

This is the most puzzling thing to me, and the spur to ask this question and clear up all my confusion about PCHes. I had planned to copy/use the IDE's default settings, but I don't want to until I understand what they are doing.

2. PCH Wizard

Since 2010, the IDE has included a precompiled header wizard. I haven't ever been able to get it to work - I am running it again right now to get its results and explain my memory of "doesn't work", but it seems to take several hours, so I will update this question later.

Edit: it runs, though it takes several hours, and produced a list of (to me, knowing the source base) odd headers. My recollection of trying it several years ago is that it didn't run at all - a definite improvement.

Since it exists, it may be the best way to set up using precompiled headers in a newly created project file formed to upgrade the 2010 project. How do I best do so? Will all the .cpp files including PchApp.h confuse it?

Questions

With that as background, I have the following questions:

  • Existing settings. I am creating a new project file and adding thousands of pre-existing .cpp files, all with "#include PchApp.h; #pragma hdrstop" at the top. Should I copy the existing RS2010 PCH settings? Should I remove the above two lines and replace them with something else?
  • Using the PCH wizard: Does this, in your experience, create optimal settings? Does it include files that, if modified, will cause large areas of the project to be rebuilt (probably non-optimal for coding)? Is it possible to use on an existing project, or do items like our "#include PchApp.h" need to be stripped out before using it?
  • CPP files / units and the correct includes. Should .cpp files that use precompiled headers not include the precompiled header itself, but only the headers that the .cpp actually needs, even if the PCH includes those? What if you have our current situation, where the PchApp.h file includes several common headers and so the .cpp files don't actually include those themselves? If you remove the inclusion of PchApp.h and replace it with the subset of headers in PchApp.h that the specific .cpp files needs, should they be above or below the #pragma hdrstop? (Above, I think.) What if you then include something else above with them which is not included in the precompiled header - will it change PCH usage for that specific unit, cause the PCH to be rebuilt (performance issues?), etc?
  • Default setup: Assuming the default setup for a new project is optimal, how is best to migrate the current system to using it?
  • Non-default setup: If the default setup is not optimal, what is? This, I guess, is the key question.
  • 32 and 64-bit: Knowing that we'll move to 64-bit soon, what should we do to have precompiled headers work on both 32 and 64 bit? Should all PCH knowledge be in the project options rather than .cpp files, so different settings for 32 and 64-bit compilation?

I am seeking a clear, detailed, explanatory, guiding answer, one that clearly explains the best practice, setting options, items to include in the .cpp files, header, and/or project file, and so forth - in other words, something to clear up my by now (after all the above!) rather confused understanding. A high-quality answer that can be used as the go-to PCH reference in future by other C++Builder users in future would be excellent. I intend to add a bounty in a couple of days when I am able to.

like image 228
David Avatar asked Aug 05 '13 16:08

David


People also ask

Should you use precompiled headers?

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.

How does a precompiled header work?

The precompiled header is compiled only when it, or any files it includes, are modified. If you only make changes in your project source code, the build will skip compilation for the precompiled header. The compiler options for precompiled headers are /Y .

How do you make a precompiled header?

Configure Visual Studio to create precompiled headerschoose "All Configurations", then go to C/C++ -> Precompiled Headers, choose "Create Precompiled Header", make sure stdafx. h is the header file to use, and leave the rest to the default value.


2 Answers

  1. Existing settings. In my experience I have changed these settings usually, because if you have hundreds of files - it's just does not seem to be optimal. In xCode i.e. it's the default configuration. There should be no compilation performance difference.
  2. Using the PCH wizard Honestly I have never used it in real project, and it haven't impressed me, so just forgot about that and used manual settings.
  3. CPP files / units and the correct includes. Different IDEs have different default settings for that. What I have usually used is:
    • Inject precompiled headers automatically (no manual #include in .cpp)
    • First include appropriate header matching .cpp if one exists (myfile.cpp - then include myfile.h)
    • After that include all the specific headers that do specific job (specific lib headers, etc.)
    • In "myfile.h" include ONLY stuff that is a must. Avoid any stuff you can avoid.
    • Everything you include specifically for a particular .cpp file should be below #pragma hdrstop. Everything you want to be precompiled should be above.
  4. Default setup I don't think it's optimal. As for me it's much easier to migrate just changing a couple of options in the settings.
  5. Non-default setup As I have mentioned above - as for me the optimal set up is with automatic injection of precompiled header. More details in item 3.
  6. 32 and 64-bit haven't experienced any problems with that. It should generate own precompiled headers for every particular configuration.
like image 144
strannik Avatar answered Nov 07 '22 04:11

strannik


Here's what I do (although I am not sure if it is a good idea or not but it seems to work)

  1. Make sure Project1PCH1.h exists (where Project1 is the name of the project)
  2. Make it contain #pragma hdrstop and 2 trailing newlines (I got weird errors when I didn't have trailing newlines, maybe compiler bug)
  3. In "All Configurations" put into "Inject precompiled header file" then name "Project1PCH1.h"
  4. Do not do anything such as #include "PchApp.h" nor #pragma hdrstop in the other files.
  5. Check everything builds correctly (i.e. files have the right includes on their own merit, not relying on the injected PCH)
  6. Put some includes into the Project1PCH1.h. I use the wizard to come up with some suggestions, but you have to apply some human logic as well to get a good build.

When it's working properly in 32bit mode everything compiles lightning quick; you can tell if you have not quite got something right if you're compiling your project and one particular .cpp file takes a lot longer than the rest. The wizard makes suggestions based on how many files include the given header, but that's somewhat bogus; you need to include in it any system header (or boost header etc.) that would add significantly to the compilation time if it were not part of the PCH.

I don't bother to include my own project headers in it, just system and standard headers. That may differ for you depending on your project, IDK.

The PCH doesn't work for .c files so if you have any of those in your file you'll need to make Project1PCH1.h have #ifdef __cplusplus guards.

Also: even though bcc64 doesn't support PCH (but it does inject the file), if you do have your PCH set up right it does seem to make compilation go a fair bit faster, I'm not exactly sure why.


Things I don't understand about it yet:

  • Why does the New Project wizard autogenerate Project1PCH1.h but not actually set that in the "Inject Precompiled Header" field of Project Properties?
  • Sometimes the build fails saying it cannot open Project1PCH1.h but if I make some changes and re-save it it usually seems to fix this.
like image 42
M.M Avatar answered Nov 07 '22 05:11

M.M