2005-02-09 (Wed) [長年日記]
_ エラー出力用マクロで可変長引数を使わずに__FILE__や__LINE__を埋め込む
Subversionのコードを読んでいて、GCC拡張やC99の可変長引数マクロを使わずに、 エラー出力用マクロで__FILE__や__LINE__を埋め込む方法を発見。
まず、次のようにエラー出力用の関数を用意しておく。
error.c:
#include <stdio.h> #include <stdarg.h> static const char *error_file = NULL; static int error_line = -1; void my_error_locate(const char *file, int line) { error_file = file; error_line = line; } void my_error(const char *fmt, ...) { va_list ap; fprintf(stderr, "%s:%d: ", error_file, error_line); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); }
そして、おもむろに次のようなマクロを定義。
error.h:
void my_error_locate(const char *file, int line); void my_error(const char *fmt, ...); #define error (my_error_locate(__FILE__, __LINE__), my_error)
あとは、このマクロをprintfのように使えばよい。
error_test.c:
#include <stdio.h> #include "error.h" int main() { error("error - %d", 12345); return 0; }
コンパイル時に-ansi -pendanticを付けても文句を言われない。
$ gcc -ansi -pedantic -Wall error.c error_test.c -o error_test
実行すると、当然ちゃんとファイル名と行番号が出る。
$ ./error_test error_test.c:7: error - 12345
念のため動作原理を説明すると、
error("error - %d", 12345);
は展開されると、
(my_error_locate("error_test.c", 7), my_error)("error - %d", 12345);
のようになる。
my_error_locate()
はstaticな変数にファイル名と行番号を格納する。
問題はその後だ。
カンマ演算子は左から右に評価されて、最後に評価された式の値が全体の値となる。
したがって、(..., my_error)
の値はmy_error
の値、すなわち、
my_error()
を指す関数ポインタになる。
その後で括弧が続いているということは・・・
そう、関数ポインタを経由したmy_error()
の呼び出しになっているのだ。
#define error (*(my_error_locate(__FILE__, __LINE__), my_error))
と書いた方がわかりやすいかもしれない。
で、my_error()
の中では、my_error_locate()
が値をセットしたstaticな
変数を参照しているので、ファイル名と行番号をちゃんと表示できるというわけ。
よくできてますよね。
ちなみに、スレッドセーフじゃないんじゃ・・・というのが誰もが考えるところ だろうが、Subversionのコードには以下のようなコメントが書いてあった。
void svn_error__locate (const char *file, long line) { /* XXX TODO: Lock mutex here */ error_file = file; error_line = line; }
この方法でソース中に埋め込んだ全デバックを、一括で無効にしたい場合にいい方法はありますか?