This article is meant for those programmers who are only getting started with the Visual Studio environment and trying to compile their C++ projects under it. Everything looks strange and complicated in an unfamiliar environment, and novices are especially irritated by the stdafx.h file that causes strange errors during compilation. Pretty often it all ends in them diligently turning off all precompiled headers in every project. We wrote this article to help Visual Studio newcomers to figure it all out.
Precompiled headers are intended to speed up project builds. When getting started with Visual C++, programmers usually try it on very small projects that cannot show the performance gain from using precompiled headers. Both with and without them, the program seems to take the same time to compile. This is just what confuses the user: he doesn't see any use in this option and concludes that it is needed for some specific tasks and he will never need it. This delusion may last for years.
Precompiled headers are actually a very useful technology. You can notice the benefit even with a project of just a few dozens of files. Using such heavy libraries as boost makes the performance gain especially evident.
If you examine the *.cpp files in your project, you will notice that many of them include the same sets of headers, for example <vector>, <string>, <algorithm>. These headers, in their turn, include other headers, and so on.
All this results in the compiler's preprocessor doing the same work again and again - it must read the same files many times, insert them into each other, process #ifdef and expand macros. Because of that, the same operations are repeated a huge number of times.
The amount of work the preprocessor has to do during project compilation can be greatly reduced. The idea is to preprocess a group of files in advance and then simply insert already prepared text fragments where necessary.
It actually includes a few more steps: instead of simple text you can store more highly processed information. I don't know how exactly it all is implemented in Visual C++, but I know that, for instance, you can store text already split into lexemes. It will speed up the compilation process even more.
A file containing precompiled headers has the ".pch" extension. The file name usually coincides with the project name, but you can naturally change this and any other used names in the settings. The *.pch file may be pretty large, which depends on how many headers are expanded in it. In PVS-Studio, for example, it occupies about 3 Mbytes.
The *.pch file is created as a result of the stdafx.cpp file's compilation. This file is built with the "/Yc" switch that is used specifically to tell the compiler to create precompiled headers. The stdafx.cpp file can contain one line: #include "stdafx.h".
The most interesting stuff is stored in the "stdafx.h" file. All the header files to be precompiled should be included into it. For example, below is the stdafx.h file we use in PVS-Studio (the text is abridged for the article):
|
|
The "#pragma warning" directives are necessary to get rid of warnings generated on standard libraries.
Now the "stdafx.h" file should be included into all the *.c/*.cpp files. You should also remove from these files all the headers already included into "stdafx.h".
But what to do when different files use somewhat similar but still different sets of headers? For example:
Should you create individual precompiled headers? Well, you can do that but you don't need to.
You only need to create one precompiled header where <vector>, <string> and <algorithm> will be expanded. The benefit of the preprocessor not having to read numbers of files and insert them into each other overweighs the losses on syntax analysis of additional code fragments.
When starting a new project, Visual Studio's Wizard creates two files: stdafx.h and stdafx.cpp. It is through them that the mechanism of precompiled headers is implemented.
These files can actually have any other names; it's not the name that matters but the compilation parameters you specify in the project settings.
A *.c/*.cpp file can only use one precompiled header. However, one project may contain a few different precompiled headers. Suppose we have only one for now.
So if you have used the Wizard, the files stdafx.h and stdafx.cpp are already created for you, and all the necessary compilation switches are also defined.
If you didn't use the precompiled headers option in your project, let's find out how to enable it. I suggest the following algorithm:
Now we have enabled the precompiled headers option. If we run compilation now, the compiler will create the *.pch file. However, compilation will terminate just a bit later because of errors.
We have set all the *.c/*.cpp files to use precompiled headers, but it's not enough. We need now to add #include "stdafx.h" into each file.
The "stdafx.h" header must be the very first one to be included into the *.c/*.cpp file. This is obligatory! Otherwise you are guaranteed to get compilation errors.
It really makes sense, if you come to think of it. When the "stdafx.h" file is included in the very beginning, you can substitute an already preprocessed text into the file. This text stays the same all the time and is not affected by anything.
And now imagine that we have included some other file prior to "stdafx.h" and that file contains the line #define bool char. It will make the situation undefined as we have changed the contents of all the files where "bool" is mentioned. Now you can't just insert a preprocessed text, and the entire mechanism of "precompiled headers" gets broken. I believe this to be one of the reasons why "stdafx.h" must be included in the first place. Perhaps there are some other reasons too.
Manually typing #include "stdafx.h" into all the *.c/*.cpp files is pretty tiresome and boring. Besides, you will get a new revision in the version control system with lots of files changed. It's no good doing so.
Third-party libraries included into the project as source files cause some additional troubles. Changing these files won't make sense. The best solution would be to disable precompiled headers for them, but it's inconvenient when you use a number of small libraries. You will be constantly stumbling over precompiled headers.
But there is an easier way to handle precompiled headers. This method is not a universal one, but it did help me in many cases.
Instead of manually adding #include "stdafx.h" in all the files, you may use the "Forced Included File" option.
Go to the "Advanced" settings tab. Select all configurations. In the field "Forced Included File" write the following text:
StdAfx.h;%(ForcedIncludeFiles)
From now on, "stdafx.h" will be automatically included in the beginning of ALL the files to be compiled. PROFIT!
You won't need to manually add #include "stdafx.h" in the beginning of each and every *.c/*.cpp file anymore - the compiler will do it automatically.
This is a very important question. Mindlessly including every single header into "stdafx.h" will slow down the compilation process instead of speeding it up.
All the files that include "stdafx.h" depend on its contents. Suppose "stdafx.h" includes the file "X.h". Changing "X.h" just a little bit may cause complete recompilation of the whole project.
Important rule. Make sure your "stdafx.h" file includes only those files that never or VERY rarely change. The best candidates are headers from system and third-party libraries.
If you include you own project files into "stdafx.h", be especially careful. Include only those files that change very, very rarely.
If any of the *.h files changes once in a month, it's too frequent enough. In most cases, it takes you more than once to do all the necessary edits in an h-file - usually 2 or 3 times. Completely recompiling the entire project 2 or 3 times is quite an unpleasant thing, isn't it? Besides, all your colleagues will need to do the same.
But don't be too fanatical about non-changing files. Include only those headers that you use really often. Including <set> won't make sense if you need it in just a couple of files. Instead, simply include this file where needed.
What for may we need several precompiled headers in one project? Well, it's a pretty rare situation indeed. But here you are couple of examples.
Imagine the project is using both *.c and *.cpp files together. You can't use a shared *.pch file for them - the compiler will generate an error.
You have to create two *.pch files. One of them is created after compiling the C-file (xx.c), the other after compiling the C++-file (yy.cpp). Accordingly, you should specify in the settings to use one precompiled header for C-files and another for C++-files.
Note. Don't forget to set different names for these two *.pch files. Otherwise they will be replacing each other.
Here's another situation. One part of the project uses one large library while the other part uses another large library.
Naturally, different parts of the project should not know about both libraries: there may be (unlucky) overlapping of entities' names in different libraries.
It is logical to create two precompiled headers and use them in different parts of the program. As we have already mentioned, you may use any names you like for the files the *.pch files are generated from. Well, even the name of the *.pch file can be changed too. It all should be done very carefully of course, but there's nothing especially difficult about using two precompiled headers.
Now that you have attentively read the text above, you will understand and eliminate any errors related to stdafx.h. But I suggest that we quickly review novice programmers' typical mistakes once again and investigate the reasons behind them. Practice makes perfect.
You are trying to compile a file that uses a precompiled header while the corresponding *.pch file is missing. Possible reasons are:
The error text says it all if you bother to read it. The file is compiled with the /Yu switch. It means that a precompiled header is to be used, but "stdafx.h" is missing from the file.
You need to add #include "stdafx.h" into the file.
If you can't do it, do not use the precompiled header for this *.c/*.cpp file. Delete the /Yu switch.
The project contains both C (*.c) and C++ (*.cpp) files. You cannot use a shared precompiled header (*.pch file) for them.
Possible solutions:
You must have done something wrong. For example, the line #include "stdafx.h" is not the first one in the file.
Take a look at this example:
|
|
This code will fail to compile, the compiler generating a seemingly strange error message:
error C2065: 'A' : undeclared identifier
It thinks that all text before #include "stdafx.h" (including this line) is a precompiled header. When compiling the file, the compiler will substitute the text before #include "stdafx.h" with the text from the *.pch file. It will result in losing the line "int A = 10".
The correct code should look like this:
|
|
One more example:
|
|
The contents of the file "my.h" won't be used. As a result, you won't be able to use the functions declared in this file. Such behavior does confuse programmers a lot. They try to "cure" it by completely disabling precompiled headers and then come up with stories about how buggy Visual C++ is. Remember one thing: a compiler is one of the least buggy tools. In 99.99% of all cases, it's not the compiler you should be angry with, but mistakes in your own code (Proof).
To avoid such troubles, make sure you add #include "stdafx.h" in the very beginning of the file ALL THE TIME. Well, you can leave comments before #include "stdafx.h"; they don't take part in compilation anyway.
Another way is to use Forced Included File. See the section "Life hack" above.
You have added into stdafx.h a file that you keep regularly editing. Or you could have included an auto-generated file by mistake.
Attentively examine the contents of the "stdafx.h" file: it must contain only headers that never or very rarely change. Keep in mind that while certain included files do not change themselves, they may contain references to other *.h files that do.
You may sometimes come across an issue when an error doesn't disappear even after fixing the code. The debugger reports something strange.
This issue may relate to the *.pch file. For some reason the compiler doesn't notice that one of the header files has been changed, so it doesn't recompile the *.pch file and keeps inserting previously generated text. It might have been caused by some faults related to the time of file modification.
This is an EXTREMELY rare situation. But it is possible and you should know about it. Personally I have faced this issue only 2 or 3 times during the many years of my career. It can be solved by complete full project recompilation.
This is the most frequent trouble users report to our support service. For details, see the documentation: "PVS-Studio: Troubleshooting". Here I will only give a brief summary of the problem.
If a solution compiles well, it doesn't mean that it is implemented correctly. One solution may often contain numbers of projects, each of them using their own precompiled headers (i.e. their own stdafx.h and stdafx.cpp files).
Troubles occur when programmers start using files from one project in another. It may be convenient and this method is quite popular indeed. But they also forget that the *.cpp file contains the line #include "stdafx.h".
The question is, which of the stdafx.h files will be taken up? If the program compiles well, it means the programmer is just lucky enough.
Unfortunately, it is very difficult for us to reproduce the behavior when using the *.pch file. You see, the "honest" preprocessor works quite differently.
You can check if your solution is implemented in a wrong way by temporarily disabling precompiled headers. You may then get lots of interesting errors that will make you sincerely wonder how your project could compile at all.
Again, refer to the documentation for details. If anything is still unclear, ask our support service.
As you can see, working with precompiled headers is pretty easy. Programmers that try to use them and constantly face "compiler's numerous bugs" just don't understand the working principles behind this mechanism. I hope this article has helped you to get rid of that misunderstanding.
Precompiled headers are a very useful option that allows you to significantly enhance project compilation speed.