6

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

  1. 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" ?

  2. What does author mean by "when standard include guards are used" ?

Thanks for your time and help.

SirDarius
  • 41,440
  • 8
  • 86
  • 100
venkysmarty
  • 11,099
  • 25
  • 101
  • 184
  • 3
    They're using a [reserved identifier](http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier). Books shouldn't violate naming rules. – chris Jul 23 '13 at 08:54
  • Not sure about this, and although it's not "standard" the #pragma once directive is a commonly implemented include guard technique, see http://en.wikipedia.org/wiki/Pragma_once, so maybe they were talking about it. – SirDarius Jul 23 '13 at 08:56
  • What the Author describes regarding your 1. question is usually irrelevant nowadays (modern preprocessors will cache the already opened include files). – πάντα ῥεῖ Jul 23 '13 at 08:57
  • Large is relative. Modern compilers are quite clever in terms of include guards, and especially with pre-compiled headers supported by pretty much all compilers you can generally ignore this. The one time you will have an impact is when you have a lot of templates in the header file that you are instantiating, then you want to look at using "extern templates" to avoid multiple instantiations and quicker compiles – Delta_Fore Jul 23 '13 at 08:58
  • @SirDarius, Not standard, but I think MSVC, GCC, and Clang all support it now, and if one doesn't, it's required to not give an error. – chris Jul 23 '13 at 09:04

6 Answers6

8

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:

  1. it means: opening a file which is not needed (since it has been already included; which you will know because the include guard is already defined) is costy; and this might be an issue if you do it a lot of times (which can happen if you have hundreds of files in your project).
  2. as opposed to using non-redundant compile guards.

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"
Stefano Falasca
  • 8,837
  • 2
  • 18
  • 24
7

I don't know what Lakos96 says, but I'm going to guess anyway...

A standard include guard is like:

foo.h

#ifndef FOO_H_INCLUDED
#define FOO_H_INCLUDED
....
#endif

A redundant include guard is using the macro when including the file:

bar.c

#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.

Community
  • 1
  • 1
rodrigo
  • 94,151
  • 12
  • 143
  • 190
6

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:

  1. 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.

  2. 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?"

doctorlove
  • 18,872
  • 2
  • 46
  • 62
  • I don't think that recent MSVC's are bad. MSVC 6 was bad yes. – bkausbk Jul 23 '13 at 09:08
  • @bkausbk MSVC still refuses to be standard conform. You can still do horrible things like passing pointers of temporaries around without a single word of MSVC. To my knowledge, you cannot even turn off these internal extensions... This qualifies as a weak compiler for me. – nikolas Jul 23 '13 at 09:11
  • H.Sutter wrote this in 2000. Back then it was partially true, i guess. Nowadays, I doubt he thinks the same about his "own" compiler. – SChepurin Jul 23 '13 at 09:19
  • 2
    http://tinodidriksen.com/2011/08/31/cpp-include-speed/ for speed measurements that show it doesnt matter at all today, if it ever did. – PlasmaHH Jul 23 '13 at 09:31
  • @nijansen: Please can you describe it further? What do you mean by "passing pointers of temporaries around"? I'm very sure that MSVC will output some warnings (which may be supressable). Btw. to show warnings or not is no sign of being standard conform or not. – bkausbk Jul 23 '13 at 10:10
  • @nijansen I loved that part too :) – Stefano Falasca Jul 23 '13 at 14:33
1

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.

SteveLove
  • 3,137
  • 15
  • 17
1

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".

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
0
  1. 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

  2. Don't know. I would guess it coud mean "when you do it every time, consistently, it becomes your coding standard".

Jan Spurny
  • 5,219
  • 1
  • 33
  • 47