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;
}
この方法でソース中に埋め込んだ全デバックを、一括で無効にしたい場合にいい方法はありますか?