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

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…