debug printf のように可変長引数の関数に対してマクロで __FILE__
と __LINE__
を勝手に埋め込みたいという要求があります。
こういうときは通常可変長引数マクロを使用しますが、世の中には可変長引数マクロが使えない環境というのがあるのです。
そういうときは次のようにします。
方法
# include <stdio.h>
# include <stdlib.h>
# include <stdarg.h>
static const char* err_file = NULL;
static int err_line = 0;
void
dprintf_locate(const char* file, int line)
{
err_file = file;
err_line = line;
}
void
dprintf_impl(const char* fmt, ...)
{
va_list ap;
fprintf(stderr, "[%s:%d] ", err_file, err_line);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, "\n");
}
ヘッダはこうなります。
void dprintf_locate(const char* file, int line);
void dprintf_impl(const char* fmt, ...);
# define dprintf (dprintf_locate(__FILE__, __LINE__), dprintf_impl)
これで可変長引数マクロで定義した時と同じように使えます。
# include "dprintf.h"
int
main(void)
{
dprintf("error!! n=%d\n", 123);
//=> "[sample.c:6] error!! n=123\n"
}
何故これで動くのか
呼び出しを展開すると
(dprintf_locate(__FILE__, __LINE__), dprintf_impl)("error!! n=%d\n", 123);
になります。
この (dprintf_locate(__FILE__, __LINE__), dprintf_impl)
の部分は C 言語のカンマ演算子で繋げられた式なので、左右の式を評価した後で最終的な値は右辺の値となります。つまりこの部分は dprintf_locate(__FILE__, __LINE__)
を実行した後で関数ポインタである dprintf_impl
を返します。
そうなると残りの部分は (dprintf_impl)("error!! n=%d\n", 123)
となってこれは関数ポインタを介した関数呼び出しです。 dprintf_impl
は内部でファイルと行数を参照しているので上記が実現できることになります。
マルチスレッドでの注意点
お察しの通りこれはスレッドセーフではありません。用途に応じてロックが必要です。