可変個数引数マクロの作り方
関数形式マクロの引数の数は固定、というのはよく知られていると思うけど、現在のC言語(C99)ではサポートされている。
// (1) #define DEBUG_PRINT(...) printf(__VA_ARGS__)
C++の仕様には入っていないけど、まあGCCではC++でも使えている。
ただ、__VA_ARGS__ はすこし凝ったことをするには限界があることを最近知った。例えば、この DEBUG_PRINT() の表示に関数名を追加したいとする。
// (2) #define DEBUG_PRINT(fmt, ...) \ printf("%s(): "fmt, __func__, __VA_ARGS__)
__func__ は関数名の文字列を指す特別な識別子で、これもC99。マクロに似ているけどマクロではないので、__FILE__や__LINE__と同じ感覚では使えない。
// (3) これは使うとコンパイルエラー #define DEBUG_PRINT(...) printf(__func__"(): "__VA_ARGS__)
さて、(2) の定義でだいたいうまく行くけど、フォーマット文字列 fmt につづく引数が1個以上必要という問題がある。これが頭痛い。
// (2) の定義を使って int func(int x) { DEBUG_PRINT("x = %d\n", x); // printf("%s(): ""x = %d\n", __func__, x); に展開される。 ... DEBUG_PRINT("end\n"); // printf("%s(): ""end\n", __func__, ); に展開される。 // 引数の最後にコンマが余計なのでコンパイルエラー。
マクロの第2引数が ... なので省略することも可能、という仕様で、__VA_ARGS__ は空に展開される。でも、引数の有無によってコンマの有無を変えたい、ということができない。
いろいろ考えたけど、現在のプリプロセッサの仕様では、
- マクロの引数が空かどうかを判定(して展開内容を変えたり)できない。
- 可変個数マクロの引数の個数を判定できない。
という限界がある模様。
仕方なく、こんな逃げ方を考えた。
// (4) これで解決〜。C99準拠。 #define DEBUG_PRINT(...) DEBUG_PRINT2(__VA_ARGS__, "") #define DEBUG_PRINT2(fmt, ...) \ printf("%s(): "fmt"%s", __func__, __VA_ARGS__)
引数の最後に "" を強制的に追加することで、1個以上の引数を保証してみた。これで解決。ただちょっとへんてこ。
そこで、GNUのCPPのマニュアルを見てみたら、便利な拡張を発見。
// (5) GCC 限定 #define DEBUG_PRINT(fmt, ...) \ printf("%s(): "fmt, __func__, ## __VA_ARGS__)
これで後ろの引数がないときコンマも削除される。この拡張なんとなく場当たり的な気もするけど便利。
ところで「今どきprintfデバッグかよ」というつっこみは無しで。