Is there a way to use pre-compiled headers in VC++ without requiring stdafx.h?
Asked Answered
S

9

11

I've got a bunch of legacy code that I need to write unit tests for. It uses pre-compiled headers everywhere so almost all .cpp files have a dependecy on stdafx.h which is making it difficult to break dependencies in order to write tests.

My first instinct is to remove all these stdafx.h files which, for the most part, contain #include directives and place those #includes directly in the source files as needed.

This would make it necessary to turn off pre-compiled headers since they are dependent on having a file like stdafx.h to determine where the pre-compiled headers stop.

Is there a way to keep pre-compiled headers without the stdafx.h dependencies? Is there a better way to approach this problem?

Sevastopol answered 14/11, 2008 at 13:13 Comment(3)
Personally I use precompiled headers (for both normal code and unit tests) but use the name Precompiled.h/cpp. The precompiled headers for the unit tests include additional headers so that the compile is faster.Orten
PLEASE VOTE FOR THIS: visualstudio.uservoice.com/forums/121579-visual-studio/…Decrement
Yes, there is a better way. Only one precompiled header can be used for each source file, but the pch can be specified fore each source file or set of source files. You can also specify which source files use or don't use a precompiled header. See my answer below.Barby
S
9

Yes, there is a better way.

The problem, IMHO, with the 'wizard style' of precompiled headers is that they encourage unrequired coupling and make reusing code harder than it should be. Also, code that's been written with the 'just stick everything in stdafx.h' style is prone to be a pain to maintain as changing anything in any header file is likely to cause the whole codebase to recompile every time. This can make simple refactoring take forever as each change and recompile cycle takes far longer than it should.

A better way, again IMHO, is to use #pragma hdrstop and /Yc and /Yu. This enables you to easily set up build configurations that DO use precompiled headers and also build configurations that do not use precompiled headers. The files that use precompiled headers don't have a direct dependency on the precompiled header itself in the source file which enables them to be build with or without the precompiled header. The project file determines what source file builds the precompiled header and the #pragma hdrstop line in each source file determines which includes are taken from the precompiled header (if used) and which are taken directly from the source file... This means that when doing maintenance you would use the configuration that doesn't use precompiled headers and only the code that you need to rebuild after a header file change will rebuild. When doing full builds you can use the precompiled header configurations to speed up the compilation process. Another good thing about having the non-precompiled header build option is that it makes sure that your cpp files only include what they need and include everything that they need (something that is hard if you use the 'wizard style' of precompiled header.

I've written a bit about how this works here: http://www.lenholgate.com/blog/2004/07/fi-stlport-precompiled-headers-warning-level-4-and-pragma-hdrstop.html (ignore the stuff about /FI) and I have some example projects that build with the #pragma hdrstop and /Yc /Yu method here: http://www.lenholgate.com/blog/2008/04/practical-testing-16---fixing-a-timeout-bug.html .

Of course, getting from the 'wizard style' precompiled header usage to a more controlled style is often non-trivial...

Strainer answered 15/11, 2008 at 23:21 Comment(1)
Please see: #7283353 - thanks.Racquelracquet
W
4

When you normally use precompiled headers, "stdafx.h" serves 2 purposes. It defines a set of stable, common include files. Also in each .cpp file, it serves as a marker as where the precompiled headers end.

Sounds like what you want to do is:

  • Leave precompiled header turned on.
  • Leave the "stdafx.h" include in each .cpp file.
  • Empty out the includes from "stdafx.h".
  • For each .cpp file, figure out which includes were needed from the old "stdafx.h". Add these before the #include "stdafx.h" in each .cpp file.

So now you have the minimal set of dependancies, and you still are using precompiled headers. The loss is that you are not precompiling your common set of headers only once. This would be a big hit for a full rebuild. For development mode, where you are only recompiling a few files at a time, it would be less of a hit.

Weald answered 14/11, 2008 at 14:7 Comment(1)
Thanks. I decided on a variation of this approach. stdafx.h is only allowed to contain #includes of external libraries (windows headers, STL, boost, etc.) All internal app headers must be included directly in .cpp files.Sevastopol
C
2

No, there is probably NOT a better way.

However, for a given individual .cpp file, you might decide that you don't need the precompiled header. You could modify the settings for that one .cpp file and remove the stdafx.h line.

(Actually, though, I don't how the pre-compiled header scheme is interferring with the writing of your unit tests).

Carissacarita answered 14/11, 2008 at 13:18 Comment(1)
Yes, there is a better way. See my answer below. @Len Holgate's answer also works.Barby
M
2

No. pre-compiled headers relies on a single header included by all sources compiled this way. you can specify for a single source (or all) not to use pre-compiled headers at all, but that's not what you want.

In the past, Borland C++ compiler did pre-compilation without a specific header. however, if two sources files included the same headers but at different order, they were compiled separately, since, indeed, the order of header files in C++ can matter...

Thus it means that the borland pre-compiled headers did save time only if you very rigidly included sources in the same order, or had a single include file included (first) by all other files... - sounds familiar ?!?!

Motorbus answered 14/11, 2008 at 13:18 Comment(1)
Not true; you can specify a different precompiled header for each individual source file, or specify that some souce files use one and others don't.Barby
C
2

Yes. The "stdafx.h/stdafx.pch" name is just convention. You can give each .cpp its own precompiled header. This would probably be easiest to achieve by a small script to edit the XML in your .vcproj. Downside: you end up with a large stack of precompiled headers, and they're not shared between TU's.

Possible, but smart? I can't say for sure.

Crosley answered 14/11, 2008 at 14:43 Comment(0)
Z
1

My advice is - don't remove precompiled headers unless you want to make your builds painfully slow. You basically have three options here:

  1. Get rid of precompiled headers (not recommended)
  2. Create a separate library for the legacy code; that way you can build it separately.
  3. Use multiple precompiled headers within a single project. You can select individual C++ files in your Solution Explorer and tell them which precomiled header to use. You would also need to setup your OtherStdAfx.h/cpp to generate a precompiled header.
Zachary answered 14/11, 2008 at 13:19 Comment(0)
I
1

Pre-compiled headers are predicated on the idea that everything will include the same set of stuff. If you want to make use of pre-compiled headers then you have to live with the dependencies that this implies. It comes down to a trade-off of the dependencies vs the build speed. If you can build in a reasonable time with the pre-compiled headers turned off then by all means do it.

Another thing to consider is that you can have one pch per library. So you may be able to split up your code into smaller libraries and have each of them have a tighter set of dependencies.

Impose answered 14/11, 2008 at 14:53 Comment(0)
S
0

I only use pre-compiled headers for the code that needs to include the afx___ stuff - usually just UI, which I don't unit-test. UI code handles UI and calls functions that do have unit-tests (though most don't currently due to the app being legacy).

For the bulk of the code I don't use pre-compiled headers.

G.

Stabile answered 14/11, 2008 at 13:20 Comment(0)
B
0

Precompiled headers can save a lot of time when rebuilding a project, but if a precompiled header changes, every source file depending on the header will be recompiled, whether the change affects it or not. Fortunately, precompiled headers are used to compile, not link; every source file doesn't have to use the same pre-compiled header.

pch1.h:

#include <bigHeader1.h>
#include ...


pch1.cpp:

#include "pch1.h"


source1.cpp:

#include "pch1.h"
[code]


pch2.h:

#include <bigHeader2.h>
#include ...


pch2.cpp:

#include "pch2.h"


source2.cpp

#include "pch2.h"
[code]

Select pch1.cpp, right click, Properties, Configuration Properties, C/C++, Precompiled Headers.
Precompiled Header : Create(/Yc)
Precompiled Header File: pch1.h
Precompiled Header Output File: $(intDir)pch1.pch

Select source1.cpp
Precompiled Header : Use(/Yu)
Precompiled Header File: pch1.h
Precompiled Header Output File: $(intDir)pch1.pch (I don't think this matters for /Yu)

Do the same thing for pch2.cpp and source2.cpp, except set the Header File and Header Output File to pch2.h and pch2.pch. That works for me.

Barby answered 20/2, 2016 at 3:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.