Is an #include before #ifdef/#define Include-Guard okay?
Categories:
Is an #include Before #ifdef/#define Include-Guard Okay?

Explore the implications of placing an #include
directive before an include guard in C++ header files, understanding potential issues and best practices for robust code.
Include guards are a fundamental mechanism in C and C++ to prevent multiple inclusions of the same header file within a single compilation unit. They typically involve a #ifndef
, #define
, and #endif
block. A common question arises regarding the placement of other preprocessor directives, specifically #include
, relative to these guards. This article delves into whether placing an #include
before the include guard is acceptable, the problems it might cause, and the recommended practices.
Understanding Include Guards
Include guards work by defining a unique preprocessor macro the first time a header file is processed. Subsequent attempts to include the same header will find this macro already defined, causing the preprocessor to skip the entire content of the header until the #endif
. This prevents redefinitions of types, functions, and variables, which would otherwise lead to compilation errors.
// Recommended structure for a header file (my_header.h)
#ifndef MY_HEADER_H
#define MY_HEADER_H
// All header content goes here
#include <vector>
class MyClass {
public:
void doSomething();
};
#endif // MY_HEADER_H
Standard include guard implementation
The Problem with #include Before Guards
Placing an #include
directive before the include guard can lead to subtle and hard-to-debug issues. The primary problem is that the content of the pre-guard #include
will always be processed, regardless of whether the main header file has already been included. This defeats the purpose of the include guard for that specific included file and can lead to multiple definition errors if the pre-guard included file itself lacks proper guards or contains definitions.
flowchart TD A[Source File .cpp] B[#include "my_header.h"] C[my_header.h] D[#include <dependency.h>] E[#ifndef MY_HEADER_H] F[#define MY_HEADER_H] G[Header Content] H[#endif] A --> B B --> C C --> D D --> E E -- No --> H E -- Yes --> F F --> G G --> H subgraph Problematic Scenario C_problem[my_header.h] D_problem[#include <dependency.h>] E_problem[#ifndef MY_HEADER_H] F_problem[#define MY_HEADER_H] G_problem[Header Content] H_problem[#endif] C_problem --> D_problem D_problem --> E_problem E_problem -- No --> H_problem E_problem -- Yes --> F_problem F_problem --> G_problem G_problem --> H_problem end style D_problem fill:#f9f,stroke:#333,stroke-width:2px linkStyle 3 stroke:#f00,stroke-width:2px,fill:none; linkStyle 4 stroke:#0f0,stroke-width:2px,fill:none; linkStyle 5 stroke:#0f0,stroke-width:2px,fill:none; linkStyle 6 stroke:#0f0,stroke-width:2px,fill:none; linkStyle 7 stroke:#0f0,stroke-width:2px,fill:none; linkStyle 8 stroke:#0f0,stroke-width:2px,fill:none; linkStyle 9 stroke:#f00,stroke-width:2px,fill:none; A -- Multiple inclusions --> B_second[#include "my_header.h"] B_second --> C_problem C_problem -- Always processed --> D_problem D_problem -- Can cause redefinition --> E_problem classDef problematic fill:#f9f,stroke:#333,stroke-width:2px; class D_problem problematic; class D problematic; class E problematic; class F problematic; class G problematic; class H problematic; class C_problem problematic; class E_problem problematic; class F_problem problematic; class G_problem problematic; class H_problem problematic; class B_second problematic; class C problematic; class B problematic; class A problematic;
Flowchart illustrating the problematic scenario of #include before include guards
Edge Cases and Exceptions
While generally discouraged, there are very specific, rare scenarios where an #include
before an include guard might be considered. One such case is when the included file defines macros that are intended to control the behavior of the include guard itself, or when it defines a macro that is part of the include guard name. However, such practices are highly unconventional and can severely reduce code readability and maintainability. It's almost always better to define such controlling macros externally or refactor the header structure.
// A highly discouraged, problematic example
#include "version_macros.h" // Defines MY_LIB_VERSION
#ifndef MY_HEADER_V_##MY_LIB_VERSION_H
#define MY_HEADER_V_##MY_LIB_VERSION_H
// ... header content ...
#endif // MY_HEADER_V_##MY_LIB_VERSION_H
An example of a problematic pre-guard include for conditional compilation
Best Practices for Header Inclusion
The universally accepted best practice is to place all #include
directives inside the include guard. This ensures that all dependencies are processed only once when the header is first included, adhering to the principle of least surprise and maximizing compilation efficiency. This also makes the header's dependencies explicit and contained within its guarded scope.
1. Define Unique Guard
Start your header file with a unique #ifndef
and #define
pair. A common convention is PROJECT_DIRECTORY_FILENAME_H
(e.g., MYPROJECT_HEADERS_MYCLASS_H
).
2. Include Dependencies
Place all necessary #include
directives immediately after your #define
guard. This includes both standard library headers and other project headers.
3. Add Declarations/Definitions
Follow with your class declarations, function prototypes, constant definitions, and any other content that belongs in the header.
4. Close Guard
End your header file with #endif
corresponding to your initial #ifndef
.