Reputation: 11431
I am reading a book on Applied C++.
Include guards will prevent a header file from being included more than once during the compilation of source file. Your symbol names should be unique, and we recommend choosing the name based on the name of the file. For example, our file, cache.h contains this include guard.
#ifndef _cache_h_
#define _cache_h_
...
#endif // _cache_h_
Lakos describes using redundant include guards to speed up compilation. See [Lakos96]. For large projects, it takes times to open each file, only to find that the include guard symbol is already defined (i.e., the file has already been included). The effects on compilation time can be dramatic, and Lakos shows a possible 20x increase in compilation times when only standard include guards are used.
[Lakos96]: LargeScale C++ software design.
I don't have Lakos96 reference book to refer concept so asking help here.
My questions on above text is
What does author mean by " For large projects, it takes times to open each file, only to find that the include guard symbol is already defined" ?
What does author mean by "when standard include guards are used" ?
Thanks for your time and help.
Upvotes: 6
Views: 2748
Reputation: 9097
From C++ Coding Standards (Sutter, Alexandrescu)
Many modern C++ compilers recognize header guards automatically (see Item 24) and don't even open the same header twice. Some also offer precompiled headers, which help to ensure that often-used, seldom-changed headers will not be parsed often
So, I would consider those suggestions outdated (unless you are still using some very dated compiler).
As for your questions:
What is a redundant compile guard?
A naive compiler will reload the file every time it's included. To avoid that, put RedundantIncludeGuards around the include: header.h
#ifndef HEADER_H_
#define HEADER_H_
// declarations
#endif
foo.c
#ifndef HEADER_H_
#include "header.h"
#endif
read more here. Your reference claims that by doing so you can be as much as 20% faster during compilation than you would be if foo.c were only doing
#include "header.h"
Upvotes: 8
Reputation: 3207
In a large project, there may be many headers - perhaps 100s or even 1000s of files. In the normal case, where include guards are inside each header, the compiler has to check (but see below) the contents of the file to see if it's already been included.
These guards, inside the header, are "standard".
Lakos recommends (for large projects) putting the guards around the #include
directive, meaning the header won't even need to be opened if it's already been included.
As far as I know, however, all modern C++ compilers support the #pragma once
directive, which coupled with pre-compiled headers means the problem is no longer an issue in most cases.
Upvotes: 1
Reputation: 5528
in larger projects with more people, there may be, for example, one module dealing with time transformation and it's author could chose to use TIME
as a guard. Then you'll have another one, dealing with precise timing and it's author, unaware of the first one, may choose TIME
too. Now you have a conflict. If they used TIME_TRANSFORMATION
and PRECISE_TIMING_MODULE
, they'll be ok
Don't know. I would guess it coud mean "when you do it every time, consistently, it becomes your coding standard".
Upvotes: 0
Reputation: 19232
Lakos' book is old. It may have been true once, but you should time things on your machine. Many people now disagree with him, e.g. http://www.allankelly.net/static/writing/overload/IncludeFiles/AnExchangeWithHerbSutter.pdf or http://c2.com/cgi/wiki?RedundantIncludeGuards or http://gamearchitect.net/Articles/ExperimentsWithIncludes.html
Herb Sutter, C++ guru and current chair of the ISO C++ standards committee, argues against external include guards:
"Incidentally, I strongly disagree with Lakos' external include guards on two grounds:
There's no benefit on most compilers. I admit that I haven't done measurements, as Lakos seems to have done back then, but as far as I know today's compilers already have smarts to avoid the build time reread overhead--even MSVC does this optimization (although it requires you to say "#pragma once"), and it's the weakest compiler in many ways.
External include guards violate encapsulation because they require many/all callers to know about the internals of the header -- in particular, the special #define name used as a guard. They're also fragile--what if you get the name wrong? what if the name changes?"
Upvotes: 6
Reputation: 129344
I think what it refers to is to replicate the include guard outside of the header file, e.g.
#ifndef _cache_h_
#include <cache.h>
#endif
However, if you do this, you'll have to consider that header guards are sometimes changing within a file. And you certainly won't see a 20x improvement in a modern system - unless all your files are on a very remote network drive, possibly - but then you'll have a much better improvement from copying the project files to your local drive!
There was a similar question a while back, regarding "including redundant files" (referring to including header files multiple times), and I built a smallish system with 30 source files, which included <iostream>
"unnecessarily", and the overall difference in compile time was 0.3% between including and not including <iostream>
. I believe this finding shows the improvement in GCC that "automatically recognises files that produce nothing outside of include guards".
Upvotes: 1
Reputation: 98348
I don't know what Lakos96 says, but I'm going to guess anyway...
A standard include guard is like:
#ifndef FOO_H_INCLUDED
#define FOO_H_INCLUDED
....
#endif
A redundant include guard is using the macro when including the file:
#ifndef FOO_H_INCLUDED
#include "foo.h"
#endif
That way the second time the foo.h
file is included, the compiler will not even search for it in the disk. Hence the speedup: imagine a large project, one single compilation unit may include foo.h
100 times, but only the first one will be parsed. The other 99 times it will be searched for, opened, tokenized, discarded by the pre-compiler and closed.
But note that that was in 1996. Today, GCC, to give a well known example, has specific optimizations that recognize the include guard pattern and makes the redundant include guard, well..., redundant.
Upvotes: 7