この記事のポイント

  • 「そもそもインクルードガードとは何か」をストーリーで理解する
  • レビューアーが読み解くべき設計意図とガード設計の関係を整理する
  • 依存統制・責務分離・CI統制まで含めたレビュー能力を養う

実務では「インクルードガードはただのおまじない」として書かれているコードも多い。
だがレビューアーは構文ではなく設計を読む訓練が必要になる。


1. なぜインクルードガードが必要になるのか?

1-1: C++のヘッダーファイルは「貼り付け機械」

C++における#includeはリンクや参照ではない。
コンパイル前処理(プリプロセッサ)の段階で文字列の貼り付けが行われるだけである。

#include "ApiRequestLog.h"

この命令は実際には:

struct ApiRequestLog {
    int requestId;
    std::string endpoint;
    int responseCode;
    std::string clientIp;
    std::string requestedAt;
};

と展開されるだけである。


1-2: 何が問題になるのか?——二重インクルード事故

複数のヘッダーが同じファイルを含むと、貼り付けが複数回行われる。

#include "ApiRequestLog.h"
#include "ApiRequestLog.h"

実際の展開:

struct ApiRequestLog { ... };  // 1回目
struct ApiRequestLog { ... };  // 2回目

これが原因でコンパイルエラーが発生する:

error: redefinition of ‘struct ApiRequestLog’

定義が重複してはならないというC++のルールに違反するためである。


1-3: 実務でこの事故がどう発生するか

  • 依存先ヘッダーが多段で包含される
  • 誰かが意図せず複数箇所で#includeする
  • 新人エンジニアが多段依存に気付かずインクルードする

→ プロジェクトが成長するほど発生頻度は高まる。


1-4: インクルードガードは「貼り付け抑制装置」

事故を防ぐため、一度読んだら二度目以降はスキップするという防御策が必要になる。
それがインクルードガードである。

#ifndef API_REQUEST_LOG_H
#define API_REQUEST_LOG_H

// ヘッダー内容

#endif
  • 最初の読み込みではAPI_REQUEST_LOG_Hは未定義 → 貼り付け実行
  • 以後の読み込みでは既定義 → スキップ

2. 実は設計レビューでここを読める人は少ない

2-1: 単なる構文防御ではない

  • 識別子設計がレビュー対象になる
  • 責務分離と依存関係設計が可視化される
  • ビルド統制の健全性が露出する

レビューアーはインクルードガードを見るときに、
「なぜその識別子なのか」「なぜそのヘッダー構造なのか」
まで読解できるべきである。


3. APIログ設計モデルを題材にレビューを進める

本記事では以下の構造体を中心に解説する。

struct ApiRequestLog {
    int requestId;
    std::string endpoint;
    int responseCode;
    std::string clientIp;
    std::string requestedAt;
};

これは実務レビューでも登場頻度の高いAPIアクセス記録用データモデルである。


4. インクルードガードと#pragma onceの選択基準

4-1: インクルードガード

#ifndef API_REQUEST_LOG_H
#define API_REQUEST_LOG_H

struct ApiRequestLog {
    int requestId;
    std::string endpoint;
    int responseCode;
    std::string clientIp;
    std::string requestedAt;
};

#endif
  • メリット:全環境可搬性、標準仕様、安全性高い
  • デメリット:識別子命名管理が煩雑

4-2: pragma once

#pragma once

struct ApiRequestLog {
    int requestId;
    std::string endpoint;
    int responseCode;
    std::string clientIp;
    std::string requestedAt;
};
  • メリット:簡潔、記述漏れが発生しにくい
  • デメリット:ファイルパスの一意性依存、極端な特殊環境での可搬性リスク

4-3: プロジェクト設計方針

対象 推奨方式
OSSライブラリ・移植性重視 インクルードガード
社内プロダクト統一 pragma once
CI厳密統制あり pragma once + CI統制

5. PlantUMLで可視化するヘッダー依存構造

UML Diagram
  • 循環依存レビューの補助になる
  • 不要インクルード発見にも役立つ

6. 良い実装例

#pragma once

struct ApiRequestLog {
    int requestId;
    std::string endpoint;
    int responseCode;
    std::string clientIp;
    std::string requestedAt;
};
設計意図
  • プロジェクト全体でpragma once統一済み
  • CIでサポート状況確認済み
  • 人為ミスが最小化されている

7. レビュー観点を整理する

✅ 命名規約統一

  • 識別子は常にファイルパス由来で命名
  • 例:API_REQUEST_LOG_H

✅ ガード漏れ防止

  • clang-tidy導入
  • include-what-you-use導入

✅ 依存整理

  • 循環依存発生予兆をレビューで検知
  • ヘッダー肥大化を防ぐ設計分離確認

8. 良くない実装例:ケース別レビュー

ケース1: 命名不足

#ifndef LOG_H
#define LOG_H
@Reviewer
識別子が汎用的すぎます。命名規約準拠のユニーク識別子にしてください
struct ApiRequestLog { int requestId; std::string endpoint; int responseCode; std::string clientIp; std::string requestedAt; }; #endif

改善例

#ifndef API_REQUEST_LOG_H
#define API_REQUEST_LOG_H

struct ApiRequestLog {
    int requestId;
    std::string endpoint;
    int responseCode;
    std::string clientIp;
    std::string requestedAt;
};

#endif

ケース2: pragma once混在

#pragma once
#ifndef API_REQUEST_LOG_H
#define API_REQUEST_LOG_H
@Reviewer
pragma onceとインクルードガードが混在しています。どちらかに統一してください
struct ApiRequestLog { int requestId; std::string endpoint; int responseCode; std::string clientIp; std::string requestedAt; }; #endif

改善例

#pragma once

struct ApiRequestLog {
    int requestId;
    std::string endpoint;
    int responseCode;
    std::string clientIp;
    std::string requestedAt;
};

ケース3: ガード漏れ

struct ApiRequestLog {
    int requestId;
    std::string endpoint;
    int responseCode;
    std::string clientIp;
    std::string requestedAt;
};
@Reviewer
インクルードガードが存在しません。ビルド時に多重定義エラーが発生します

改善例

#pragma once

struct ApiRequestLog {
    int requestId;
    std::string endpoint;
    int responseCode;
    std::string clientIp;
    std::string requestedAt;
};

9. CI統制とビルド安定性

検査 ツール 説明
ガード漏れ検出 clang-tidy modernize-use-pragma-once
無駄インクルード検出 include-what-you-use 循環抑止
混在禁止検出 スクリプト検査 PR時自動チェック
識別子統一支援 テンプレート生成 ヒューマンエラー抑止

10. レビュー観点チェックリスト


まとめ

インクルードガード設計は、設計読解力を試されるレビュー領域である。

レビューアーは構文的正当性の確認を超えて
依存設計・責務分離・命名意図・ビルド統制まで読み解く力を磨く必要がある。

本稿のAPIログモデルを通じ、
「なぜインクルードガードが存在するのか」
というレビューの本質に立ち戻ってほしい。