LoginSignup
2
3

More than 5 years have passed since last update.

printfマクロで可変長引数を使わずに__FILE__や__LINE__を入れる方法

Posted at

debug printf のように可変長引数の関数に対してマクロで __FILE____LINE__ を勝手に埋め込みたいという要求があります。

こういうときは通常可変長引数マクロを使用しますが、世の中には可変長引数マクロが使えない環境というのがあるのです。

そういうときは次のようにします。

方法

dprintf.c
#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");
}

ヘッダはこうなります。

dprintf.h
void dprintf_locate(const char* file, int line);
void dprintf_impl(const char* fmt, ...);

#define dprintf (dprintf_locate(__FILE__, __LINE__), dprintf_impl)

これで可変長引数マクロで定義した時と同じように使えます。

sample.c
#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 は内部でファイルと行数を参照しているので上記が実現できることになります。

マルチスレッドでの注意点

お察しの通りこれはスレッドセーフではありません。用途に応じてロックが必要です。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3