テキストマクロの置換
プリプロセッサはテキストマクロ置換をサポートしています。関数形式のテキストマクロ置換もサポートされています。
目次 |
[編集] 構文
#define 識別子 置換リスト (オプション) |
(1) | ||||||||
#define 識別子 (パラメータ ) 置換リスト (オプション) |
(2) | ||||||||
#define 識別子 (パラメータ , ...) 置換リスト (オプション) |
(3) | (C++11以降) | |||||||
#define 識別子 (...) 置換リスト (オプション) |
(4) | (C++11以降) | |||||||
#undef 識別子 |
(5) | ||||||||
[編集] 説明
[編集] #define ディレクティブ
#define ディレクティブは、識別子をマクロとして定義します。つまり、コンパイラに識別子のほとんどの連続する出現を置換リストに置き換えるように指示し、これはさらに処理されます。スキャンと置換のルールから例外が生じます。識別子が既に何らかの種類のマクロとして定義されている場合、定義が同一でない限り、プログラムは不正な形式です。
[編集] オブジェクト形式マクロ
オブジェクト形式マクロは、定義された識別子のすべての出現を置換リストに置き換えます。#define ディレクティブのバージョン (1) は、まさにそのように動作します。
[編集] 関数形式マクロ
関数形式マクロは、定義された識別子の各出現を置換リストに置き換え、さらにいくつかの引数を取り、それらの引数は置換リスト内のパラメータの対応する出現を置き換えます。
関数形式マクロ呼び出しの構文は関数呼び出しの構文と似ています。マクロ名の後に次のプリプロセスートークンとして(が続く各インスタンスは、置換リストによって置き換えられるトークンのシーケンスを導入します。シーケンスは、対応する左右の括弧の間のペアをスキップして、対応する)トークンで終了します。
バージョン (2) の場合、引数の数はマクロ定義のパラメータの数と同じでなければなりません。バージョン (3,4) の場合、引数の数はパラメータの数より少なくあってはなりません (...を含まない(C++20以降))。そうでなければ、プログラムは不正な形式です。識別子が関数表記でない場合、つまり、その後に括弧がない場合、全く置換されません。
#define ディレクティブのバージョン (2) は、単純な関数形式マクロを定義します。
#define ディレクティブのバージョン (3) は、可変個引数を持つ関数形式マクロを定義します。追加の引数 (可変引数と呼ばれます) は__VA_ARGS__識別子を使用してアクセスでき、これは置き換えられる識別子に供給される引数に置き換えられます。
#define ディレクティブのバージョン (4) は、可変個引数を持つ関数形式マクロを定義しますが、通常の引数はありません。引数 (可変引数と呼ばれます) は__VA_ARGS__識別子のみでアクセスでき、これは置き換えられる識別子に供給される引数に置き換えられます。
|
バージョン (3,4) の場合、置換リストにはトークンシーケンス #define F(...) f(0 __VA_OPT__(,) __VA_ARGS__) F(a, b, c) // replaced by f(0, a, b, c) F() // replaced by f(0) #define G(X, ...) f(0, X __VA_OPT__(,) __VA_ARGS__) G(a, b, c) // replaced by f(0, a, b, c) G(a, ) // replaced by f(0, a) G(a) // replaced by f(0, a) #define SDEF(sname, ...) S sname __VA_OPT__(= { __VA_ARGS__ }) SDEF(foo); // replaced by S foo; SDEF(bar, 1, 2); // replaced by S bar = { 1, 2 }; |
(C++20以降) |
注:関数形式マクロの引数に、対応する左右の括弧のペアによって保護されていないコンマが含まれる場合 (テンプレート引数リストで最もよく見られます。例えばassert(std::is_same_v<int, int>);やBOOST_FOREACH(std::pair<int, int> p, m)のように)、そのコンマはマクロ引数のセパレータとして解釈され、引数カウントの不一致によりコンパイルエラーが発生します。
[編集] スキャンと置換
- スキャンは、置き換えたマクロを追跡します。スキャンがそのようなマクロと一致するテキストを見つけると、「無視する」とマークします(すべてのスキャンがそれを無視します)。これにより、再帰を防ぎます。
- スキャンが関数形式マクロを見つけた場合、引数は置換リストに入れる前にスキャンされます。ただし、# および## 演算子はスキャンなしで引数を取ります。
- マクロが置換された後、結果のテキストがスキャンされます。
擬似再帰マクロを定義できることに注意してください。
#define EMPTY #define SCAN(x) x #define EXAMPLE_() EXAMPLE #define EXAMPLE(n) EXAMPLE_ EMPTY()(n-1) (n) EXAMPLE(5) SCAN(EXAMPLE(5))
出力
EXAMPLE_ ()(5 -1) (5) EXAMPLE_ ()(5 -1 -1) (5 -1) (5)
[編集] 予約済みマクロ名
標準ライブラリヘッダをインクルードする翻訳単位は、任意の標準ライブラリヘッダで宣言された名前を#defineまたは#undefすることはできません。
標準ライブラリのいかなる部分を使用する翻訳単位も、語彙的に同一な名前を#defineまたは#undefすることは許可されていません。
|
(C++11以降) |
それ以外の場合、動作は未定義です。
[編集] # および ## 演算子
関数形式マクロでは、置換リスト内の識別子の前の#演算子は、識別子をパラメータ置換にかけて結果を引用符で囲み、実質的に文字列リテラルを作成します。さらに、プリプロセッサは、埋め込み文字列リテラルがある場合は、それを囲む引用符をエスケープするためにバックスラッシュを追加し、必要に応じて文字列内のバックスラッシュを二重にします。先頭と末尾のすべての空白は削除され、テキストの中央にある(ただし埋め込み文字列リテラル内ではない)空白のシーケンスは単一のスペースにまとめられます。この操作は「文字列化 (stringification)」と呼ばれます。文字列化の結果が有効な文字列リテラルでない場合、動作は未定義です。
|
#が #define showlist(...) puts(#__VA_ARGS__) showlist(); // expands to puts("") showlist(1, "x", int); // expands to puts("1, \"x\", int") |
(C++11以降) |
置換リスト内の2つの連続する識別子の間の##演算子は、2つの識別子に対して(最初にマクロ展開されない)パラメータ置換を実行し、その結果を連結します。この操作は「連結 (concatenation)」または「トークン貼り付け (token pasting)」と呼ばれます。一緒に有効なトークンを形成するトークンのみが貼り付けられます。例えば、より長い識別子を形成する識別子、数値を形成する数字、または+と=が+=を形成するような演算子です。コメントは/と*を貼り付けて作成することはできません。なぜなら、マクロ置換が考慮される前にコメントはテキストから削除されるためです。連結の結果が有効なトークンでない場合、動作は未定義です。
注:一部のコンパイラは、##をコンマの後ろ、__VA_ARGS__の前に置くことができる拡張機能を提供しています。この場合、可変引数が存在するときは##は何もしませんが、可変引数が存在しないときはコンマを削除します。これにより、fprintf (stderr, format, ##__VA_ARGS__)のようなマクロを定義することが可能になります。これは、__VA_OPT__を使用して標準的な方法でも実現できます。例えばfprintf (stderr, format __VA_OPT__(, ) __VA_ARGS__)のように。(C++20以降)
[編集] #undef ディレクティブ
#undef ディレクティブは識別子を未定義にします。つまり、#define ディレクティブによる識別子の以前の定義を取り消します。識別子に関連付けられたマクロがない場合、ディレクティブは無視されます。
[編集] 定義済みマクロ
以下のマクロ名は、すべての翻訳単位で定義済みです。
| __cplusplus |
使用されているC++標準のバージョンを示し、値に展開されます。
|
| __STDC_HOSTED__ (C++11) |
実装がホスト型(OS上で動作)の場合、整数定数1に展開され、スタンドアロン型(OSなしで動作)の場合、0に展開されます。 (マクロ定数) |
| __FILE__ |
現在のファイル名を文字の文字列リテラルとして展開します。#lineディレクティブによって変更できます。(マクロ定数) |
| __LINE__ |
現在の物理ソース行の行番号を整数定数として展開します。#lineディレクティブによって変更できます。(マクロ定数) |
| __DATE__ |
翻訳の日付を"Mmm dd yyyy"形式の文字文字列リテラルとして展開します。"dd"の最初の文字は、月の日が10未満の場合、スペースになります。月の名前はstd::asctime()によって生成されるかのようです。 (マクロ定数) |
| __TIME__ |
翻訳時刻を"hh:mm:ss"形式の文字文字列リテラルとして展開します。 (マクロ定数) |
| __STDCPP_DEFAULT_NEW_ALIGNMENT__ (C++17) |
値がアライメント非対応のoperator newの呼び出しによって保証されるアライメントであるstd::size_tリテラルに展開されます(より大きなアライメントは、operator new(std::size_t, std::align_val_t)のようなアライメント対応のオーバーロードに渡されます)。 (マクロ定数) |
| __STDCPP_BFLOAT16_T____STDCPP_FLOAT16_T____STDCPP_FLOAT32_T____STDCPP_FLOAT64_T____STDCPP_FLOAT128_T__ (C++23) |
実装が対応する拡張浮動小数点型をサポートする場合に限り、1に展開されます。 (マクロ定数) |
以下の追加マクロ名は、実装によって定義される場合があります。
| __STDC__ |
実装定義の値。存在する場合、通常C適合性を示すために使用されます。 (マクロ定数) | ||||
| __STDC_VERSION__ (C++11) |
実装定義の値。存在する場合。 (マクロ定数) | ||||
| __STDC_ISO_10646__ (C++11) |
(マクロ定数) | ||||
| __STDC_MB_MIGHT_NEQ_WC__ (C++11) |
基本文字セットのメンバー(例えばwchar_tにUnicodeを使用するEBCDICベースのシステムなど)に対して'x' == L'x'がfalseになる可能性がある場合、1に展開されます。 (マクロ定数) | ||||
| __STDCPP_THREADS__ (C++11) |
プログラムが複数の実行スレッドを持つことができる場合、1に展開されます。 (マクロ定数) |
| __STDCPP_STRICT_POINTER_SAFETY__ (C++11)(C++23で削除) |
実装が厳格なstd::pointer_safetyを持つ場合、1に展開されます。 (マクロ定数) |
これらのマクロの値(__FILE__と__LINE__を除く)は、翻訳単位全体で一定のままです。これらのマクロを再定義または未定義しようとすると、未定義の動作になります。
言語機能テストマクロ標準では、C++11以降で導入されたC++言語機能に対応するプリプロセッサマクロのセットが定義されています。これらは、当該機能の存在を検出するためのシンプルで移植性の高い方法を意図しています。 詳細については、機能テストを参照してください。 |
(C++20以降) |
注釈関数ローカルな定義済み変数__func__は定義済みマクロではありませんが、通常は__FILE__や__LINE__とともに、例えばassertなどで使用されます。 |
(C++11以降) |
[編集] 例
#include <iostream> // Make function factory and use it #define FUNCTION(name, a) int fun_##name() { return a; } FUNCTION(abcd, 12) FUNCTION(fff, 2) FUNCTION(qqq, 23) #undef FUNCTION #define FUNCTION 34 #define OUTPUT(a) std::cout << "output: " #a << '\n' // Using a macro in the definition of a later macro #define WORD "Hello " #define OUTER(...) WORD #__VA_ARGS__ int main() { std::cout << "abcd: " << fun_abcd() << '\n'; std::cout << "fff: " << fun_fff() << '\n'; std::cout << "qqq: " << fun_qqq() << '\n'; std::cout << FUNCTION << '\n'; OUTPUT(million); //note the lack of quotes std::cout << OUTER(World) << '\n'; std::cout << OUTER(WORD World) << '\n'; }
出力
abcd: 12 fff: 2 qqq: 23 34 output: million Hello World Hello WORD World
[編集] 欠陥報告
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| CWG 2908 | C++98 | __LINE__が現在の物理行番号に展開されるのか、論理行番号に展開されるのか不明であった。 |
現在の 物理行番号に展開されます |
| LWG 294 | C++98 | 標準ライブラリヘッダをインクルードする翻訳単位は、 他の標準ライブラリヘッダで宣言された名前を定義するマクロを含みうる |
禁止された |
| P2621R2 | C++23 | ユニバーサル文字名が許可されていなかった トークン連結によって形成されること |
許可 |
[編集] 関連項目
| C++ドキュメントのマクロシンボルインデックス
| |
| テキストマクロの置換に関するC言語ドキュメント
|