/FI, STLPort, Precompiled headers, warning level 4 and #pragma hdrstop

| 6 Comments
The one where I find that you can teach an old dog new tricks and almost use the 'rocks' word.

Just recently I've switched to using STLPort on two of the code bases that I'm working on. One of my clients wanted to use it because they were having some weird threading issues with the standard VC6 stl and their 'enterprise' install of VC was too hard to patch with updated stl headers from dinkumware... (I'm starting to think that 'enterprise' often means big, clunky, broken and incompetent, but that's another story). Anyway, since we could install STLPort as 'just another library' we could switch code to using it without needing to worry about getting the VC6 install updated and repackaged for x thousand dev boxes... It worked and I liked the debugging iterator stuff in STLPort so I added support for using it with The Server Framework.

Whilst I was doing this work I noticed that the STLPort gave some different (ignorable) warnings than the dinkumware STL which meant that our #pragma warning adjustments needed fixing up. Since I was looking into warning levels, etc, and since we'd had a little surprise earlier in the week when we found some simple bugs in a related code base that we expected the compiler to be issuing warnings for, we also decided to switch on warning level 4 at the same time.

Warning level 4 is one of those things that I've been meaning to enable on the JetByte Tools code for some time. The need to silence the really useless warnings with #pragma has slowed me down but since I was looking into the issue for my client I thought I might as well deal with our own code too.

The first thing I found out was that /FI is your friend. The client code base is pretty big and their 'enterprise' (see above) ClearCase install makes checking out and checking in again every single file in the project a time consuming pain. We'd collected a set of #pragmas into a "Warnings.h" header file and needed to include it in every source file to turn off the noise. Using /FI you can 'force include' a header into every source file in a project just by editing the dsp... /FI tells the compiler to include the file at 'line 0' in the source file. Problem solved, for the time being, we could add the warning suppression header to all files without going through CC hell and test the warning level 4 and STLPort idea to see if it would fly. It did.

I've never been much of a fan of the 'standard' way of using precompiled headers in VC. I like my code to depend on just the headers that it needs. It means you KNOW when you suddenly start using a type that you weren't previously coupled to because you have to physically add the include for it and the using directives, etc. IMHO precompiled headers through #include "stdafx.h" is evil, probably higher on the list of devil spawn than even the dreaded #import.

The way I tend to structure code made me think that I wouldn't gain a great deal with precompiled headers and they certainly weren't worth the coupling if I had to do them via the #include "stdafx.h" route. The discovery of the wonders of /FI changed all that... Using /FI you can create a 'precompiled header' include file that includes the world and then /FI it into all files in a build configuration. Set /Yu"PrecompiledHeader.h" (use precompiled header file through header) and add a new cpp file that builds the precompiled header with /Yc"PrecompiledHeader.h" (create precompiled header file through header) and you're away. What's more you can have other build configurations without precompiled headers so you can make sure that you aren't relying on the evil 'include the world' header. The build seems faster, which is good, but it wasn't especially obvious how you could tune what you include in the precompiled header so that you achieve maximum goodness.

Then I found #pragma hdrstop. The notes on MSDN say that "The hdrstop pragma gives you additional control over precompilation file names and over the location at which the compilation state is saved." which is true, but it's not especially obvious exactly how cool that can be...

In a nutshell, putting #pragma hdrstop in a source file that isn't compiled with /Yc or /Yu has no effect at all. If you have /Yu set for the file then hdrstop tells the compiler to throw away everything before the line on which hdrstop appears and insert the precompiled header instead. If /Yc is set for the file then hdrstop means save all of the compiled state for everything up to the line on which hdrstop appears as the precompiled header. The trick is using /Yc and /Yu without the optional header file name; just check the 'use' or 'create' radio button and leave the 'through header' edit box blank (or edit the dsp).

This does away with the need to /FI your precompiled header into the source file. Simply add #pragma hdrstop to the file at the point where you want to use the precompiled header, set /Yu and you're away. Of course this means editing every file, so I've done this for our tools libs because we use CVS and it's not as painful as 'enterprise' CC... The client code is being converted as and when, one library at a time. The advantage of the #pragma hdrstop route over the /FI route is that you can finely tune the headers that end up in the precompiled header. If a single source file uses a huge and complex suite of headers and none of the other source files do (common if you're using pimpl compilation firewalls) then you can put the hdrstop above these headers and leave them out of the precompiled header. In summary, you can tune the precompiled header to give you the biggest bang for your buck.

For some reason I feel the need to add 'kewl', 'rocks' and 'longhorn' to this blog entry...

6 Comments

Thanks, very helpful!

I'm curious... have you measured the timing differences between turning precompiled headers on and off for a build? For me, it's usually two to five object files that have to be rebuilt while I'm developing, and I don't see a benefit from using PCH at all. I have seen a benefit for a complete rebuild, but that's usually an opportunity for a coffee/tea break anyways. What sort of impact do you notice on your code-build-test cycle?

FYI, it may help you to note how I use PCHs (works for both GCC and MSVC). Every source file includes a local header first (your "stdafx.h", but I usually call it "$libname-pre.h" or something similar). That header includes every 3rd-party header file I will have to use, but it does not include any project-specific header files (so no local changes require me to rebuild the PCH). The source files still #include everything else they need, so it's easy to #ifdef out all of the #includes in the PCH header.

Maybe I'm doing it wrong?

Tom,

I compared timings and precompiled headers were faster for complete builds and no precompile header was faster for general development. I have a lot of libraries that build on each other. I often find that I need to tweak or add to lower level libs whilst working on new projects and the ripple on effect of having a precompiled header in place for that was too much. Plus I like the fact that I can build without them to prove my code only includes what it needs to and DOES include all it needs to.

On my continuous integration build server I build every configuration, for each compiler, for each project, so shaving a bit of time off of each build with the precompiled header is worth it.

The problem with the 'everyone includes the precompiled header' approach is that you have to build with precompiled headers. I prefer my way as I get to decide at compile time if I want them on or off.

Hi Len,

Can a faster build be achieved using precompiled headers with #include "stdafx.h" + #pragma hdrstop versus only precompiled headers with #include "stdafx.h"?

Yes, the point is, using #pragma hdrstop lets you have a fast full build, using precompiled headers and a fast partial build (not using precompiled headers). The problem, IMHO, with precompiled headers using just the 'stdafx.h' style is that if you are developing or maintaining the code and changing headers often then you will rebuild more than you need to each time you compile. In these situations is good to NOT have precompiled headers turned on.

Yes, the point is, using #pragma hdrstop lets you have a fast full build, using precompiled headers and a fast partial build (not using precompiled headers). The problem, IMHO, with precompiled headers using just the 'stdafx.h' style is that if you are developing or maintaining the code and changing headers often then you will rebuild more than you need to each time you compile. In these situations is good to NOT have precompiled headers turned on.

Leave a comment

About this Entry

Practical Testing: 9 - More tests, more development, notice the order? was the previous entry in this blog.

Practical Testing: 10 - Fixing the tick count wrap bug, again is the next entry in this blog.

I usually write about C++ development on Windows platforms, but I often ramble on about other less technical stuff...

Find recent content on the main index or look in the archives to find all content.

I have other blogs...

Subscribe to feed The Server Framework - high performance server development
Subscribe to feed Lock Explorer - deadlock detection and multi-threaded performance tools
Subscribe to feed l'Hexapod - embedded electronics and robotics
Subscribe to feed MegèveSki - skiing