Does anyone use guarded include files

the pre-processor strips those lines out. the compiler never sees them

consider this text

now is the time for all good men to come to the aid of their party
#if 0
the quick brown fox jumped over the lazy dog
#endif
joe tok fathers shoe bench out

the output from gcc -E

# 0 "large.cpp"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "large.cpp"
now is the time for all good men to come to the aid of their party



joe tok fathers shoe bench out

It's not that easy - see the nowadays compiler stages. In former times the preprocessor was a self-contained program. Nowadays it's possible that a separate preprocessor will cause a differently compiled code.

1 Like

Whether the preprocessor is part of the compiler itself or a separate program, it's still going to very quickly skip over anything inside #if 0/#endif, and the compiler proper doesn't have to parse anything inside (unless it's a poorly written compiler).

Of course, this “feature” may not be present in all compilers/pre-processors. It’s an optimization added to gcc precisely because the guards are so common.

Almost all arduino boards use gcc, and it’s rather unlikely that it would be an issue with any program small enough to fit on a microcontroller.

1 Like

Read the C/C++ standards.

Exactly which part(s) of the standards are you referring to? Can a compiler not simply read and discard all lines inside a #if 0/#endif block? Or are there subtle things required by the standards that make that strategy not work?

Is the feature you are taking about #pragma once or is it an inherent compiler feature to recognize .h files by name/location and only include them once?

the issue is it's common for a .cpp file to include many .h files, which include . h files, which in turn include .h files.

the guard being discussed, for example

#ifndef SOME_H
# define SOME_H
...
#endif

causes the contents of the .h, to be ignored by the pre-processor, (not the compiler) after the first .h file is encountered and SOME_H is defined. once SOME_H is defined, all other #ifndef SOME_H will not be true

Yes, thank you, but the discussion had devolved into the relative merits of putting the guard into the program containing the .h preventing the file from being opened at the cost of an #ifndef/#endif guard around each time the .h was used OR putting the guard into the .h file so that the user was not burdened with guarding at the cost of actually opening and reading (but not compiling) the .h file.

This further devolved into a discussion of the compatibility of #pragma once as a least work solution across multiple platforms/implementations.

I misread the comment to infer that there was a "feature" preventing the .h from being read multiple times and wondered what feature was under discussion. At that time I hadn't realized we were still arguing about #ifndef being reliably implemented across all compilers an Arduino user was likely to encounter.

Regards,
Glenn

i'm not sure i read that correctly either. suggests that there's no need for "hdr guards"

the following is in recurse.h

#include "recurse.h"

i got the following truncated output

_Others gcc -E recurse.h
# 0 "recurse.h"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "recurse.h"
# 1 "recurse.h" 1
# 1 "recurse.h" 1
.
.
.
                 from recurse.h:1,
                 from recurse.h:1,
                 from recurse.h:1,
                 from recurse.h:1:
recurse.h:1:21: error: #include nested depth 200 exceeds maximum of 200 (use -fmax-include-depth=DEPTH to increase the maximum)
    1 | #include "recurse.h"
      |                     ^
402
.
.
.

No. Each line has to be checked for a #else, #elif or #endif, nested #if and ordinary comments that may hide preprocessor commands.

The preprocessor can do much more than that.

Of course. I simplified my description of how it actually works. My point is that a preprocessor should be fast (assuming it's not written poorly) since it doesn't have hardly any parsing or analyzing to do compared to the compiler proper.

I just did a quick test. x.h contains the usual header guard (#ifndef X_H/#define X_H...#endif) with 10k blank lines in between, and x.c includes x.h 10k times. It compiled in about 1-2 milliseconds. I took out the header guard, and it took about 4 seconds to compile. Then I moved the header guards to x.c (one header guard around each of the 10k #include "x.h" lines), and it also took about 1-2 milliseconds. So this is strong evidence that:

  • the preprocessor does a lot less work than the compiler proper and
  • header guards (inside the header itself) has no significant overhead vs putting the header guards in the file that includes it (not to mention the practical benefits of putting header guards in the included file itself rather than every place where the file is included, which violates the DRY principle).

(Note also that the compiler proper also doesn't have a lot of work to do with just blank lines, and the preprocessor is still about 2000x faster. Imagine how much slower the compiler would be, with no header guards, if the header contained actual code like variable/function declarations!)

I for one wouldn't sweat an extra millisecond or two, even with an extreme case of 9999 extraneous includes of the same file in a single compilation unit.

1 Like

the preprocessor is still about 2000x faster

Thank you for coming up with a credible test to resolve the question.

This is a summary of the thread to date. I've listed it as the solution so anyone checking gets the summary without reading the entire thread.

Initial question was effectively: Is preventing a .h file from being read multiple times worth while and what is the best way to provide the guarding?

There are two reasons to prevent multiple inclusions of a .h file. The primary reason is to prevent items from being defined multiple times since this usually results in compile errors. The secondary reason is to decrease compile time since the compiler has fewer lines to parse.

The obvious conclusion is that preventing compile errors is a necessary and sufficient reason to use preprocessor directives (#ifndef / #endif) to prevent multiple insertions.

There is discussion of the two basic approaches -- using the preprocessor #ifndef/#endif commands in the sketch to prevent the .h file from being opened OR using the commands inside the .h file.

The first case requires the sketch coder to individually bracket each .h file. The second approach requires the .h coder to include protection once in each .h file.

The conclusion seems to be that both work, neither is significantly faster than the other, and that for programs likely to run on an Arduino the difference is irrelevant.

A discussion of using #pragma once appeared on several other platforms and the conclusion was that, while #pragma once is not an official directive all of the current implementations support it. Unless you are writing for a very old compiler or one that you know does not support #pragma once it is an acceptable use.

Several people have put significant effort into testing preprocessor/compiler speeds so read the thread if interested in either the methods or results.

Thanks to everyone for asking questions I hadn't though of and providing answers.

3 Likes

Tests should be made on real code, not on code constructed to prove some opinion.

Correct in a test without embedded code - absolutely useless :frowning:

Based on which observation?

Did you ever try to measure the time for real code like this:

#define TEST
#ifndef TEST
  10000 lines of real code here
#endif

as opposed to 10000 empty lines?

the primary reason to prevent re-processing a .h is often to prevent multiple definition errors for the same symbol, not to save time

1 Like

OK, how about if I have:

#include "stm.h"
#ifdef WEWMUL
#include "stm.h"
#include "stm.h"
#include "stm.h"
#include "stm.h"
#include "stm.h"
#include "stm.h"
#include "stm.h"
#include "stm.h"
#include "stm.h"
#include "stm.h"
#include "stm.h"
#include "stm.h"
#include "stm.h"
#include "stm.h"
#endif
int main() {}
void _exit(int c) {while (1);}

stm.h is a copy of STM32F745xx.h from the ST Arduino project, about 17k lines of code that does additional #includes, for a total of about 23k lines.
It has traditional #ifdef XXX/#define XXX style guards.
(actually removing the guard isn't possible, because you get "man" "redeclaration" errors. As one might expect.)

Results:

> time /usr/local/gcc-arm-none-eabi-10.3-2021.07/bin/arm-none-eabi-gcc -Os -g -mcpu=cortex-m4 -mthumb foo.c

real    0m0.056s
user    0m0.037s
sys     0m0.016s

> time /usr/local/gcc-arm-none-eabi-10.3-2021.07/bin/arm-none-eabi-gcc -Os -g -mcpu=cortex-m4 -mthumb -DWEWMUL=1 foo.c

real    0m0.053s
user    0m0.036s
sys     0m0.014s

Insignificant time differences due to the multiple includes.

You still tailor test files to your goal :frowning:

Your test cases only differ in 14 lines of

#include "stm.h"

what obviously different from 10000 lines of real code.

Consider that even a preprocessor has to read each char of a file in order to find line beginnings. That alone has to make a big difference between skipping empty and non-empty lines.

Based on the time difference of 2 milliseconds with header guards vs. 4 seconds without.

However, I realized that ccache was throwing off my figures. By taking ccache out of the equation, the no-header guard test takes about 3 seconds while the header guard test takes about 9 milliseconds (not 2 milliseconds). So the preprocessor is "only" about 300 times as fast as the compiler proper.

No, I didn't have that much code at hand that is safe to include more than once (which means the code cannot define any classes or structs since that'll result in redefinition errors). but I suspect the difference in compile times to be similar or even larger as the compiler will have even more to actually process while the preprocessor will happily skip over lines of whatever code happens to be inside the header guard.

Here's a test to test my suspicion. This time I #include x.h only 100 times (otherwise it takes way too long and takes a lot of memory with 10k inclusions). Also, I'm compiling with g++ rather than gcc; g++ is slightly slower than gcc (C++ is quite a bit larger and more complicated than C). The code inside x.h is a simple extern int x;, repeated 10k times.

x.h:

#ifndef X_H
#define X_H
extern int x; // 10000 times
#endif

x.cc:

#include "x.h" // 100 times
  • With header guards in x.h: 23 milliseconds
  • With header guards in x.cc around each #include "x.h": 23 milliseconds
  • Without header guards: 1.6 seconds

So my above suspicion was wrong. The preprocessor is only about 70 times as fast as the compiler proper in this particular case. Then again, I gave the compiler fairly easy code to digest, so it'd be interesting to see it try to eat more complicated code.

Either way, even if the exact values are different, my previous conclusions are still valid:

  • The preprocessor is much faster than the compiler proper (about 70 times as fast in this case).
  • There's no significant time difference between placing header guards inside or outside an include file.
  • From a code style standpoint, it's better to place header guards inside the include file rather than everywhere it's included.

And:

  • Always use header guards if there's a possibility that your header might be included more than once. Otherwise you may get multiple definition errors which will make the code with header guards compile infinitely faster (since code with errors will never compile :smiley:).

In my test the preprocessor had to read 14 million characters (14 characters per line, 10k lines per file inclusion, 100 file inclusions), and it did so in 23 milliseconds. That's about 1.6 nanoseconds per character. I'm not too worried about it. (If I were to build this in CP/M on an 8080 with a compiler like Small C, it might take a significant amount of time (take a nice, long walk while you wait) with how primitive CP/M's file buffering and Small C's preprocessor are, but we're in the 2020s now and don't have to worry about stuff that was an issue 40 years ago.)