Include Guard V.S. #pragma once

Summary

In this post, I will introduce the difference between include guard and #pragma directives in C++.

Conclusion

As usual, I will present the conclusion first.

  1. Include guard is the standard way to prevent multiple inclusion of the same header;
  2. #pragma directive is not required by C++ standard but now widely supported by most of the compilers, including GCC and clang.
  3. Use include guard if possible;
    // use this one
    #ifndef LIBRARY_FILENAME_H
    #define LIBRARY_FILENAME_H
    // contents of the header
    #endif /* LIBRARY_FILENAME_H */
    #pragma once
    // contents of the header

Details

Preprocessors

Preprocessors are programs that process our source code before compilation and linking. There are different types of preprocessor directives:

  1. Macros;
  2. Include Guard;
  3. Conditional Compilation;
  4. Other directives like #pragma directives;

The preprocessor is executed at translation phase 4, before the compilation. Typically the preprocessor will first recursively replace all #include directives with the literal file declared in the directive (usually header files, but possibly other source files); the result of this step is a preprocessing translation unit. Then it will handle macro expansion of #define directives, and (conditional compilation)[https://en.wikipedia.org/wiki/Object_file] of #ifdef directives, among others; this translates the preprocessing translation unit into a translation unit.

The files by #include directives are generally header files, which typically contain declarations of functions and classes or structs.

Translation Unit

As introduced in the above subsection, a translation unit is the output of the preprocessor. From a translation unit, the compiler generates an object file, which can be further processed and linked (possibly with other object files) to form an executable program.

Include Guard

As you may notice, the preprocessor will recursively replace all #include directives with the literal files. There may be the case that certain classes or functions are defined twice. The resulting translation unit is invalid. See One Definition Rule for details.

#include guards prevent this erroneous construct from arising by the double inclusion mechanism.

For example,

//File "grandparent.h"
#ifndef GRANDPARENT_H
#define GRANDPARENT_H

struct foo {
    int member;
};

#endif /* GRANDPARENT_H */
//File "parent.h"
#include "grandparent.h"
//File "child.c"
#include "grandparent.h"
#include "parent.h"

Here, the first inclusion of "grandparent.h" causes the macro GRANDPARENT_H to be defined. Then, when "child.c" includes "grandparent.h" the second time, the #ifndef test returns false, and the preprocessor skips down to the #endif, thus avoiding the second definition of struct foo. The program compiles correctly.

//Result
struct foo {
    int member;
};

In order for #include guards to work properly, a project using must work out a coherent naming scheme for its include guards, and make sure its scheme doesn’t conflict with that of any third-party headers it uses, or with the names of any globally visible macros.

Pragma Once

If it appears in a header file, it indicates that it is only to be parsed once, even if it is (directly or indirectly) included multiple times in the same source file.

Unlike include guards, #pragma once makes it impossible to erroneously use the same macro name in more than one file. On the other hand, since with #pragma once files are excluded based on their filesystem-level identity, this can’t protect against including a header twice if it exists in more than one location in a project (this will happen when someone copy and paste codes).

For example,

//File "grandparent.h"
#ifndef GRANDPARENT_H
#define GRANDPARENT_H

struct foo {
    int member;
};

#endif /* GRANDPARENT_H */
//File "dir/grandparent.h"
#ifndef GRANDPARENT_H
#define GRANDPARENT_H

struct foo {
    int member;
};

#endif /* GRANDPARENT_H */
//File "parent.h"
#include "grandparent.h"
//File "child.c"
#include "grandparent.h"
#include "dir/grandparent.h"
#include "parent.h"

This will work since grandparent.h and dir/grandparent.h has the same signature GRANDPARENT_H.

However,

//File "grandparent.h"
#pragma once
struct foo {
    int member;
};
//File "dir/grandparent.h"
#pragma once
struct foo {
    int member;
};
//File "parent.h"
#include "grandparent.h"
//File "child.c"
#include "grandparent.h"
#include "dir/grandparent.h"
#include "parent.h"

This will violate the One Definition Rule.

Reference

Wikipedia Pragma Once
Wikipedia Include Guard
cppreference

Leave a Reply

Your email address will not be published. Required fields are marked *