tshinoの日記

2006/11/23(木)

[] 可変個数引数マクロの作り方 03:50

GCCプリプロセッサの便利な拡張を知ったのでメモ。

関数形式マクロの引数の数は固定、というのはよく知られていると思うけど、現在の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デバッグかよ」というつっこみは無しで。

tshinotshino 2006/11/24 20:35 よく見直したら、そもそも ... の部分に引数を与えない使い方はエラーらしい。これができるのもGCCの拡張。C99使えない…。

htzhtz 2009/04/30 00:50 なるほど!!いやぁこれはめっちゃ使えます!
ありがとうございます。
C99準拠の方使わせて頂きます。

tshinotshino 2009/04/30 02:01 どうぞ〜。個人的メモでもひとの役に立つんだなぁ。

トラックバック - http://d.hatena.ne.jp/tshino/20061123/1164307813