Post

To guard or not to guard: Header guards x pragma once

Roughly 10 years ago I started working with C++, and even though I started studying the language, its syntax, and details, there was nothing like having the code I wrote reviewed by other people to really teach me the best practices. In one of those reviews, in a newly created header file, someone said: You need to use header guards, and so I did. A quick google to see what was header guards and I learned: I needed to use header guards.

Over the years, it started to make sense to me the reason why header guards were important, but it wasn’t until a few weeks ago that I started questioning that again. More specifically, should I really be using header guards, or is #pragma once better?

Why do I even need guards?

One Definition Rule

Let’s circle back to the beginning. In C++ we must obey the One Definition Rule (ODR), a rule that states that a function or variable must have a single definition within a single program. This rule helps to prevent issues with multiple definitions of the same function or variable, which can cause errors and unintended behavior in the program. Breaking the ODR can lead to issues such as “multiple definition” errors during compilation, or even linking errors, as the linker might not be able to determine which definition of a function or variable to use.

So, having the following will result in a compilation error:

grandparent.h

1
struct foo {};

parent.h

1
#include "grandparent.h"

child.h

1
2
#include "grandparent.h"
#include "parent.h"

As, when processing child.h, the preprocessor will replace the #include directives with the content of the files, which transitively will result in:

1
2
struct foo {);
struct foo {);

To enforce the ODR, we must make use of any mechanism to ensure that a function or variable is defined only once. In very small cases, this can be achieved by controlling the #include hierarchy and avoiding a header being included in more than one place. For example, skipping the inclusion of grandparent.h in child.h in the case above. This is a very naïve technique that will take you only so far as what you can make sense of in your head.

Header guards

The most common solution is to use header guards to solve this. Header guards, or include headers, in C++ are a standard way to prevent a header file from being included more than once in a single compilation unit. They do this by using preprocessor macros to check if the header has already been included. If it has, the header guard macros prevent the header file from being included again.

In the example above, this should be as easy as changing the grandparent.h file to include the header guarder such as:

1
2
3
4
5
6
#ifndef GRANDPARENT_H
#define GRANDPARENT_H

struct foo {);

#endif /* GRANDPARENT_H */

Thus, making the result of the processed child.h file, just the definition of the struct foo.

One important attention point, especially when working with large codebases, and using header guards, is that it must come with a strong guideline on the macro name used, as using just the name of the file for example, can easily lead to name clashes. Other usual issues can arise when such copy and pasting header files and missing the required change on the macro used, or missing the #endif directive.

Tools like clang-tidy can be an ally in this case with checkers like llvm-header-guard.

Pragma once

Another widespread alternative to header guards is the use of #pragma once, which, naively, only has advantages: it’s less code, less error-prone due to avoidance of name clashes, and it can even speed up compilation, although this last one is strongly debatable and I’ll address this later.

In the example we’ve been working on, grandparent.h becomes just:

1
2
3
#pragma once

struct foo {);

Why can’t I just use #pragma once?

Well, you probably can, and many will arguably say you should. But it’s not that simple.

The main point people make when advocating for the continued use of header guards is that they are in the standard, while compilers are not required by ISO C++ to support #pragma once, although the vast majority do support it for a long time.

But, why isn’t #pragma once in the standard? The answer to this seems to lie in the complexity for a compiler to correctly and consistently detect the equality of files. One of the known caveats of #pragma once is related to symbolic links. This can be seen in GCC, and MSVC, even though both do specify to support the directive. A failure in detecting this equality can result in the file being included more than once, resulting in an ODR break.

Even for a more usual scenario, there’s still no guarantee that the support of #pragma once is the same across different compilers, which can be a deal breaker for some developers.

In the discussions, there’s also the repeated mention of the advantage of a speed-up in compilation when using #pragma once as a reason to use this alternative. This is based on the idea that the compiler will not reopen the file containing this instruction, but as far as I can tell from reading many threads of discussions, this seems to be an urban legend or a residual statement from the early days. Today, many compilers apply optimizations to improve the preprocessor performance, such as the Multiple Include Optimization from GCC which should make the possible gain of #pragma once negligible among the other stages of the build, especially in the days of different levels of caching available.

So, header guards or #pragma once?

Well, to no surprise, the answer is that it depends on each case. Based on what I learned and experienced, in the end, it boils down to the question: Is the gain from the simplicity of #pragma once worth it in your codebase/project over the problems you may face?

All I can offer is my opinion, and it’s no, it’s not worth it. Header guards aren’t great, but they are trustworthy and a very well-defined piece of the standard. Alongside checkers (such as the one from clang-tidy) mentioned beforehand, they should not cause problems.

The unreliability of #pragma once implementation, as well as the scenarios where one could reach issues that would definitely prevent the expected outcome, are just not worth it in the context I’m inserted.

In my opinion, it’s still perfectly fine to take on the usage of #pragma once regardless of its issues in specific scenarios such as a smaller codebase or when teaching. But, the developer should be aware of the reasoning behind this decision.

Final thoughts

To be honest, when starting this post, I had a very strong inclination towards #pragma once, and would even advocate in its favor. But in the end, I would take the occasional mysterious and painful compilation error due to name clashing of macros, over a directive I can’t really control.

This post is licensed under CC BY 4.0 by the author.