はじめに
C++のソースコードが、コンパイラによって機械語に翻訳される前には、「プリプロセス(前処理)」というステップが存在します。プリプロセッサは、このステップで動作し、#
で始まる特別な**ディレクティブ(指令)**に従って、ソースコードのテキストを準備・整形します。
プリプロセッサは、ヘッダーファイルの取り込み、マクロの定義、コードの一部分をコンパイル対象に含めたり除外したり(条件付きコンパイル)といった、強力な機能を提供します。
この記事では、C++プログラミングに不可欠な、主要なプリプロセッサディレクティブの使い方を網羅的に解説します。
1. #include
: ファイルの読み込み
指定したヘッダーファイルの内容を、その場に文字通りコピー&ペースト(展開)します。
// 1. 標準ライブラリのヘッダーファイル
#include <iostream>
#include <vector>
// 2. 自作のヘッダーファイル
#include "my_header.h"
// 3. ファイルが存在するかチェックしてからインクルード (C++17)
#if __has_include(<optional>)
# include <optional>
#endif
< >
: 標準ライブラリなど、システムが知っているインクルードパスから探します。" "
: プロジェクトのローカルディレクトリなど、現在のファイルからの相対パスを優先して探します。__has_include
: 特定のヘッダーが利用可能かをコンパイル時にチェックできます。
2. #define
/ #undef
: マクロの定義と解除
#define
は、特定の識別子(マクロ名)を、指定したテキストで置き換えるように指示します。
オブジェクト形式マクロ
単純な定数として利用します。
#define MAX_ITEMS 100
int my_array[MAX_ITEMS]; // int my_array[100]; に置き換えられる
注意: const
変数の方が型安全なため、定数の定義には const
や constexpr
を使うのが現代C++のベストプラク-ティスです。
関数形式マクロ
引数を取る、関数のようなマクロを定義できます。
// NG例: (5 + 2) * (5 + 2) とはならず、 5 + 2 * 5 + 2 = 17 となってしまう
// #define SQUARE(x) x * x
// OK例: 引数をカッコで囲むのが定石
#define SQUARE(x) ((x) * (x))
int result = SQUARE(5 + 2); // ((5 + 2) * (5 + 2)) に置き換えられる
特殊な演算子 (#
, ##
)
#
(文字列化): マクロの引数を文字列リテラルに変換します。#define TO_STR(a) #a
→TO_STR(Hello)
は"Hello"
になります。##
(連結): 2つのトークンを連結して、一つの新しいトークンを生成します。#define CONCAT(a, b) a##b
→CONCAT(var, 1)
はvar1
になります。
3. #if
, #ifdef
など: 条件付きコンパイル
特定の条件に基づいて、ソースコードの一部をコンパイルの対象に含めたり、除外したりします。
サンプルコード
#define RELEASE_MODE
// #define DEBUG_MODE
void setup() {
#if defined(DEBUG_MODE)
// DEBUG_MODEが定義されている場合のみ、この部分がコンパイルされる
std::cout << "デバッグモードで起動" << std::endl;
#elif defined(RELEASE_MODE)
std::cout << "リリースモードで起動" << std::endl;
#else
std::cout << "不明なモードです。" << std::endl;
#endif
// #ifdef は #if defined() の短縮形
#ifdef DEBUG_MODE
// ...
#endif
// #ifndef は #if !defined() の短縮形
#ifndef LICENSE_KEY
// LICENSE_KEYが未定義の場合にコンパイル
#endif
}
解説: OSの違い(#if _WIN32
)や、デバッグビルドとリリースビルドでコードを切り替える際に頻繁に使われます。
4. #error
/ #warning
: コンパイルエラー/警告の出力
特定の条件が満たされない場合に、コンパイルを意図的に失敗させたり、警告を出したりできます。
#if __cplusplus < 201703L
#error このプログラムはC++17以降の規格を要求します。
#endif
#ifndef API_KEY
#warning API_KEYが定義されていません。デフォルト値を使用します。
#endif
5. 定義済みマクロ
コンパイラによって、便利な情報を含むマクロが事前に定義されています。
マクロ名 | 説明 |
__FILE__ | 現在のソースファイル名(文字列) |
__LINE__ | 現在の行番号(整数) |
__DATE__ | コンパイルされた日付(文字列) |
__TIME__ | コンパイルされた時刻(文字列) |
__cplusplus | C++の規格バージョンを示す整数 |
まとめ
今回は、C++のプリプロセッサディレクティブの主要な機能について解説しました。プリプロセッサは、コードのテキストレベルでの操作を行う強力なツールであり、特にマクロと条件付きコンパイルは、移植性の高いコードや、ビルド構成に応じた柔軟なプログラムを書く上で不可欠な機能です。