Replacing text macros
プリプロセッサはテキストマクロ置換をサポートしています。関数形式のテキストマクロ置換もサポートされています。
目次 |
構文
#define
識別子 置換リスト
(オプション)
|
(1) | ||||||||
#define
識別子
(
パラメータ
)
置換リスト
(オプション)
|
(2) | ||||||||
#define
識別子
(
パラメータ
, ...)
置換リスト
(オプション)
|
(3) | (C++11以降) | |||||||
#define
識別子
(...)
置換リスト
(オプション)
|
(4) | (C++11以降) | |||||||
#undef
識別子
|
(5) | ||||||||
説明
(注:元のテキストには翻訳対象となる自然言語のテキストが含まれていないため、HTML構造のみを保持したまま出力しています)
#define ディレクティブ
#define ディレクティブは identifier をマクロとして定義し、つまりコンパイラに対して identifier の連続するほとんどの出現を replacement-list で置き換えるよう指示します。この置換リストはさらに処理されます。例外は scanning and replacement の規則から生じます。識別子が既に何らかのタイプのマクロとして定義されている場合、定義が同一でない限りプログラムは不適格です。
オブジェクト形式マクロ
オブジェクト形式マクロは、定義された identifier のすべての出現を replacement-list で置き換えます。 #define ディレクティブのバージョン(1)はこの動作を正確に実装しています。
関数形式マクロ
関数形式マクロは、定義された identifier の各出現箇所を replacement-list で置換し、さらに引数を取り、 replacement-list 内の対応する parameters の出現箇所を置換します。
関数形式マクロの呼び出しの構文は関数呼び出しの構文と類似しています:マクロ名の各インスタンスの直後に ( が次のプリプロセッシングトークンとして続く場合、そのトークン列は replacement-list によって置換されるトークン列の開始を導入します。このシーケンスは対応する ) トークンによって終了し、途中に現れる一致する左右の括弧のペアはスキップされます。
バージョン(2)では、引数の数はマクロ定義内のパラメータ数と同じでなければなりません。バージョン(3,4)では、引数の数はパラメータ数より少なくてはなりません(
以下
(C++20以降)
...
はカウントしません)。そうでない場合、プログラムは不適格となります。識別子が関数形式でない場合、つまり自身の後に括弧がない場合は、一切置換されません。
バージョン(2)の #define ディレクティブは、単純な関数形式マクロを定義します。
バージョン(3)の
#define
ディレクティブは、可変長引数を持つ関数形式マクロを定義します。追加の引数(
可変引数
と呼ばれる)は
__VA_ARGS__
識別子を使用してアクセスでき、この識別子は置換対象の識別子に指定された引数で置き換えられます。
バージョン(4)の
#define
ディレクティブは、可変長引数を持つ関数形式マクロを定義しますが、通常の引数は持ちません。引数(
可変引数
と呼ばれる)は
__VA_ARGS__
識別子でのみアクセス可能で、これはその後、置換対象の識別子に指定された引数で置き換えられます。
|
バージョン(3,4)において、
replacement-list
はトークンシーケンス
#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 ) )が含まれている場合、そのカンマはマクロ引数の区切り文字として解釈され、引数の数が合わないためにコンパイルエラーが発生します。
スキャニングとリプレースメント
- スキャンはマクロが置換された内容を追跡します。スキャンがそのようなマクロに一致するテキストを見つけた場合、それを「無視対象」としてマークします(すべてのスキャンはこれを無視します)。これにより再帰が防止されます。
- スキャンが関数形式マクロを検出した場合、引数は replacement-list 内に配置される前にスキャンされます。ただし # および ## 演算子はスキャンなしで引数を取得します。
- マクロが置換された後、結果テキストがスキャンされます。
疑似再帰マクロを定義することが可能であることに注意してください:
#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以降) |
それ以外の場合、動作は未定義です。
(注:指定された要素内には翻訳対象となるテキストコンテンツが存在しないため、HTML構造のみが保持されています)
# および ## 演算子
関数形式マクロにおいて、 # 演算子を識別子の前に置くと、 replacement-list 内の識別子がパラメータ置換を経て引用符で囲まれ、実質的に文字列リテラルが生成されます。さらに、プリプロセッサは埋め込まれた文字列リテラル(存在する場合)を囲む引用符をエスケープするためにバックスラッシュを追加し、必要に応じて文字列内のバックスラッシュを二重化します。すべての先頭および末尾の空白は削除され、テキスト中間の連続する空白(ただし埋め込まれた文字列リテラル内を除く)は単一のスペースに圧縮されます。この操作は「文字列化(stringification)」と呼ばれます。文字列化の結果が有効な文字列リテラルでない場合、動作は未定義となります。
|
#
が
#define showlist(...) puts(#__VA_ARGS__) showlist(); // expands to puts("") showlist(1, "x", int); // expands to puts("1, \"x\", int") |
(C++11以降) |
任意の2つの連続する識別子の間にある
##
演算子は、2つの識別子(マクロ展開前の状態)に対してパラメータ置換を実行し、その結果を連結します。この操作は「連結」または「トークン貼り付け」と呼ばれます。有効なトークンを形成するトークンのみが貼り付け可能です:より長い識別子を形成する識別子、数値を形成する数字、または
+=
を形成する演算子
+
と
=
。
/
と
*
を貼り付けてもコメントは作成されません。なぜならコメントはマクロ置換が行われる前にテキストから除去されるためです。連結の結果が有効なトークンでない場合、動作は未定義です。
注意: 一部のコンパイラは、カンマの後かつ
__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_EMBED_NOT_FOUND__
__STDC_EMBED_FOUND__
__STDC_EMBED_EMPTY__
(C++26)
|
それぞれ
0
、
1
および
2
に展開される
(マクロ定数) |
以下の追加のマクロ名は実装によって事前定義されている可能性があります:
|
__STDC__
|
実装定義の値(存在する場合)、通常はC準拠を示すために使用される
(マクロ定数) |
||||
|
__STDC_VERSION__
(C++11)
|
実装定義の値(存在する場合)
(マクロ定数) |
||||
|
__STDC_ISO_10646__
(C++11)
|
(マクロ定数) |
||||
|
__STDC_MB_MIGHT_NEQ_WC__
(C++11)
|
1
に展開される(基本文字セットのメンバー
x
に対して
'x'
==
L
'x'
がfalseになる可能性がある場合、例えばUnicodeを
wchar_t
に使用するEBCDICベースのシステムなど)
(マクロ定数) |
||||
|
__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> // 関数ファクトリを作成して使用 #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' // 後のマクロ定義でマクロを使用 #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); //引用符がないことに注意 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++ ドキュメント
for
マクロシンボルインデックス
|
|
|
C ドキュメント
for
テキストマクロの置換
|