In my opinion, preprocessor macros and the outdated #include
mechanism are one of the worst parts of the C++ language. It is not just these things are causing a lot of problems, even more, it is very time consuming to find them.
This article will focus on #pragma once
. In the past, I already wrote articles about how to avoid macros and why you should use namespaces.
While I usually focus on embedded development on this blog, this topic especially aplies to desktop software. It is valid not only for C++, but also C programs.
As usual, I try to cover the topic in detail to bridge any knowledge gaps you may have.
What is the Problem with Macro Header Guards?
Macro based header guards can lead to unexpected problems. In the following sections, I will explain the core concepts and demonstrate the problem using a simple example.
Compiling Units
In C and C++ a compile unit usually consists of a header (.h
or .hpp
) and implementation file (.c
or .cpp
). It is the lowest level of encapsulation. The header file contains the interface of the unit, while the cpp
or c
file contains the implementation.
The idea behind this concept is to allow “hiding” or encapsulating implementation details in a file. After the unit is compiled, you get an object file .o
or with several ones a complete library.
The user of the compiling unit or library does not have to know the code of the implementation file and uses the header to access the interface for it.
The Stream Example
The following example is a minimal program with two compile units. For the Stream
class header, old-style header guards are used.
Stream.hpp (With Old Guards)
#ifndef STREAM_H #define STREAM_H class Stream { public: void flow(); }; #endif
Stream.cpp
#include "Stream.hpp" #include <iostream> void Stream::flow() { std::cout << "murmur..." << std::endl; }
main.cpp
#include "Stream.hpp" int main() { Stream stream; stream.flow(); return 0; }
How the Example Compiles
Most software developers use comfortable IDE and therefore have normally no contact with the actual invocation of the compiler commands. If the stream example is compiled using the GNU compiler, the following commands are executed:
c++ -o Stream.o Stream.cpp c++ -o main.o main.cpp c++ -o myApp Stream.o main.o
First, the unit Stream.cpp
is compiled into Stream.o
. Next, the unit main.cpp
is compiled into main.o
, and eventually the two compiled .o
units are linked into the final application.
Please note, these processes are a simplification, so you can follow the topic more easily. It basically works in the shown way. Modern compiler introduces features like precompiled headers or link-time code generation which blur the borders between the processes a little bit.
The Preprocessor
If a unit is compiled, the code of the unit is processed by the preprocessor. It will include all the required files. resolve defined macros and put everything in one single file for the actual compiler.
Compilers usually support an option where you can get the immediate output of the preprocessor, without actually compiling the unit.
c++ -E -o main-pre.cpp main.cpp
For the main
unit, this will generate a file like the following one:
# 1 "main.cpp" # 1 "Stream.hpp" 1 class Stream { public: void flow(); }; # 2 "main.cpp" 2 int main() { Stream stream; stream.flow(); return 0; }
The preprocessor marked all processed and included files with #
comment lines. As you can see, the macro header guard is gone, because all these preprocessor statements were resolved.
First, it started with the main.cpp
file then found the #include "Stream.hpp"
which it included at this point. Afterwards, it continued the with the rest of the main.cpp
file.
It is a very simple example and therefore easy to read and understand. If you are working with libraries which are using hundreds of included files, the preprocessor result can be overwhelming long and hard to read.
Still, this is more or less what the compiler will process, in exact this order. Even modern compiler do not exactly process the files in this way, creating an immediate preprocessor file can help if you are searching for compilation issues.
Why is a Header Guard Required?
The guard is required to prevent a header file included twice in the same compiling unit. Have a look at the following dependency graph:

From the file main.cpp
the two header files Town.hpp
and Forest.hpp
are included. Both files include Stream.hpp
.

For the preprocessor, the includes look as shown above. As you can see, the file Stream.hpp
is included twice.
If the preprocessor creates the compile unit, without header guards, the result will look as shown in the following example:

As you can see, the declaration class Stream
is made twice, which will generate an error. The compiler does not know the reason for the second declaration and has no choice to stop at this point.
A header guard will detect the header Stream.hpp
was already included before and omit its contents for the second #include
.
Using a Library
The example program is working fine, and for the next iteration, we like to replace the <iostream>
of the standard library with the advanced text stream library magic_text_stream
. An inexperienced software developer developed it, and it looks like this:
magic_text_stream/ConsoleStream.hpp
#ifndef CONSOLE_STREAM_H #define CONSOLE_STREAM_H #include "Stream.hpp" namespace magic_text_stream { class ConsoleStream : public Stream { public: void write(const std::string &text) override; }; } #endif
magic_text_stream/ConsoleStream.cpp
#include "ConsoleStream.hpp" #include <iostream> namespace magic_text_stream { void ConsoleStream::write(const std::string &text) { std::cout << "Console: " << text << std::endl; } }
magic_text_stream/Stream.cpp
#ifndef STREAM_H #define STREAM_H #include <string> namespace magic_text_stream { class Stream { public: virtual void write(const std::string &text) = 0; }; } #endif
We assume for the example, the library comes precompiled, and we get a static library and the header files installed on the system.
Adding the Library to the Project
To add the new magic_text_stream
library to our project, we include the header in our implementation file. The library uses a namespace, and the class ConsoleStream
also does not conflict with our Stream
class. Therefore everything seems fine if we replace <iostream>
as shown below:
Stream.cpp
#include "Stream.hpp" #include <magic_text_stream/ConsoleStream.hpp> void Stream::flow() { magic_text_stream::ConsoleStream console; console.write("murmur..."); }
Strange Compilation Errors
If we try to compile our project, we get a really strange error:
In file included from Stream.cpp:3: magic_text_stream/ConsoleStream.hpp:10:22: error: use of undeclared identifier 'std' void write(const std::string &text) override;
So, the identifier std
is not declared? We see ConsoleStream.hpp
includes Stream.hpp
, which correctly includes <string>
, therefore the namespace std
should be known.
As you already noticed, the error is caused because the library and the application both using the STREAM_H
macro as header guard.
Mistakes were Made
Clearly, mistakes were made. The library author should have used a header guard macro, as shown below:
#ifndef MAGIC_TEXT_STREAM_STREAM_HPP #define MAGIC_TEXT_STREAM_STREAM_HPP // ... #endif
Also, the author of the application should prefix the header guards with the application name.
While fixing the problem is relatively simple, many beginners are entirely lost with this kind of compilation errors. The displayed error is completely unrelated to the actual problem. Without experience, it is hard to find and will waste hours or even days.
A Long List of Potential Problems
Using macro header guards has the potential of a long list of problems. While reviewing code, I found all of the described ones at least once.
Other than problems in the code itself, there is no compiler warning about a macro header guard which does not match the filename and namespace.
Below, the top four problems with macro header guards.
1. The name of the header guard lacks a prefix with the name of the library or namespace.
#ifndef STREAM_H #define STREAM_H <--- missing prefix // ... #endif
2. The name was not changed after changing the name of the file, class or namespace.
#ifndef APP_STREAM_H #define APP_STREAM_H <--- does not match file/class class House { // ... }; #endif
3. The name used with #ifndef
does not match the one used with #define
.
#ifndef MAGIC_TEXT_STREAM_STREAM_HPP #define MAGIC_TEXT_STREAN_STREAM_HPP // ... found it? #endif
4. The #endif
statement is missing.
#ifndef MAGIC_TEXT_STREAM_STREAM_HPP #define MAGIC_TEXT_STREAM_STREAM_HPP // ...
The Simple Solution
Using #pragma once
will delegate the task, of detecting subsequent #include
statements for the same file, to the compiler. It can do this efficiently and safely. As a user, there is no room for mistakes. Just place the directive in the first line of every header file.
Stream.hpp
#pragma once class Stream { public: void flow(); };
There is no unique name you have to write correctly and no #endif
which must exist at the end of the file. Also, if you change the name of the file or class, no changes are required.
Why is this not Part of the C++ Standard?
All #pragma
statements are meant to be compiler-specific instructions which are per definition not portable. Therefore using #pragma once
is not guaranteed to be implemented by a compiler and also, there is no standardised way how duplicate includes are detected.
Detecting duplicate includes is not always as simple as it seems: If a project uses symlinks to include libraries, the same header file may be accessed using two different absolute paths. Also, compiling has not necessarily be done on the same computer; it can be split up to many processes and executed on different servers on the network.
For these reasons, the first accepted proposal for a #pragma once
replacement was not made until 2016 with the number P0538R0. It suggests the new preprocessor directives #once
and #forget
as a qualified replacement.
Also, C++20 will most likely include a new concept of modules, which will solve the current problems with included header files.
At the time I write this it is the year 2019, and there are still compiler which do not fully implement the C++11 standard, which is eight years old. So, do not expect to have modules or the #once
statement until 2028.
Pragma Once Support Today
The #pragma once
statement is supported by almost all compiler suites, even most for embedded development. You can safely use it and expect it will work with any compiler.
A Wikipedia page will give you a list of compiler and their support of the statement.
Conclusion
I recommend using #pragma once
instead of old macro-based header guards. It will reduce the number of preprocessor macros and prevent potential and hard to find compilation problems. You should also replace existing macro-based header guards with this statement if you do maintenance work on existing code.
The statement is supported by all relevant compiler suites in a very efficient and safe way. While there may be rare problem cases, in distributed compiling environments, this will affect just a few users who are well aware of this.
I hope this article gave you some motivation to get rid of your old macro-based header guards. If you are not sure if #pragma once
will work for your project, replace the old guards using a script and try to compile it.
If you have questions, miss some information or have any feedback, feel free to add a comment below.
Learn More

Units of Measurements for Safe C++ Variables

Real Time Counter and Integer Overflow

Class or Module for Singletons?

Write Less Code using the “auto” Keyword

C++ Templates for Embedded Code (Part 2)
