More C++ Idioms/複文マクロ(Multi-statement Macro)
表示
複文マクロ(Multi-statement Macro)
[編集]意図
[編集]複文(複数行)のマクロを書く。
別名
[編集]同期
[編集]2つ以上の分をまとめて1つのマクロにして、関数呼び出しのように呼び出すことが便利な場合がある。 大抵 inline 関数が第一候補となるはずだが、デバッグ用途のようなものだとほとんど常に関数呼び出しよりもマクロになってしまう。 素朴な方法で、複数の文を 1 つのマクロにまとめた場合、ぱっと見では明らかではないコンパイルエラーを引き起こす場合がある。例えば、
#define MACRO(X,Y) { statement1; statement2; }
のようなマクロは、セミコロンが終端に付けられている場合、if 文のところでコンパイルエラーになる。
if (cond)
MACRO(10,20); // セミコロンによりここでコンパイルエラー
else
statement3;
上記の文は次のように展開され
if (cond)
{ statement1; statement2; }; // セミコロンによりここでコンパイルエラー
else
statement3;
エラーが発生する。そのため、人々は、複文マクロに対して広く使われる、do-while ループを基本とするイディオムを考え出した。
解法とサンプルコード
[編集]以下に複文マクロ(Multi-statement Macro)イディオムの例を示す。
#define MACRO(arg1, arg2) do { \
/* 必要ならば宣言を入れることもできる */ \
statement1; \
statement2; \
/* ... */ \
} while(0) /* (終端の ; がない) */
呼び出し元でセミコロンを付け加えれば、この式は文脈に関わらず単一の文になる。 最適化コンパイラは大抵 while(0) のような無意味なテストを取り除く。 このイディオムはマクロが関数呼び出しのパラメータとして呼び出される時には役に立たない。 なお、このイディオムではマクロ中で return 文を用いることができる。
func(MACRO(10,20)); // ここで構文エラー。
既知の利用
[編集]Adaptive Communication Environement (ACE) での ACE_NEW_RETURN、ACE_NEW_NORETURN マクロ。
#define ACE_NEW_RETURN(POINTER,CONSTRUCTOR,RET_VAL) \
do { POINTER = new (ACE_nothrow) CONSTRUCTOR; \
if (POINTER == 0) { errno = ENOMEM; return RET_VAL; } \
} while (0)