トップ «前の日記(2005-02-08 (Tue)) 最新 次の日記(2005-02-11 (Fri))» 編集   RSS 1.0 FEED  

Journal InTime


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;
}
Tags: C
本日のツッコミ(全1件) [ツッコミを入れる]
_ moris (2005-08-09 (Tue) 11:00)

この方法でソース中に埋め込んだ全デバックを、一括で無効にしたい場合にいい方法はありますか?