読者です 読者をやめる 読者になる 読者になる

百日半狂乱

Shut the fuck up and write some code!!

Cのエラーメッセージ出力に関数名や行番号を付加する

C/C++

二十五日半狂乱、5日目(の分)の記事

C言語における関数のエラーハンドリングには戻り値およびerrnoを使うが、自分が読んだ文法書などのサンプルコードではエラーが起こった場合の処理が、大体がperror("fopen"); exit(1);のような感じのもので、まぁ小さいサンプルコードをいくつも書いていたうちはこれでも良かった.

だけど、ある程度以上の規模のアプリケーションを書き始めて、コードがそれなりに大きくなってくると一体どこのエラーなのか一目でわからなくなってくる.

エラーメッセージを自由に設定して、関数名や行番号も表示したい.それに何度も書くのでできるならお手軽に書きたい.

そんな事情で、一年前くらい前に以下のようなエラーメッセージ用の関数を作った.コンパイラgccを使用する.

改めて見ると現在時刻は完全に蛇足な感じがするが、まぁ割と重宝している.

上記サンプルは実行すると、現在時刻、プロセスID、ファイル名、関数名、行数と一緒にメッセージを出力する*1

$ ./a.out 
Tue Dec 10 01:53:05 2013 4606 error.c main 37 error hoge Success(0)
Tue Dec 10 01:53:05 2013 4606 error.c main 38 warnning fuga:foo Success(0)

ERRORと書いたまさにその場所の関数名や行番号を出力して欲しいので、以下のようにマクロで置換している.Cではファイル名(__FILE__)や行番号(__LINE__)などの組み込みマクロが用意されている.

#define ERROR(fmt, ...) err_msg(__FILE__, __FUNCTION__, __LINE__, "error", fmt, ##__VA_ARGS__)

__FUNCTION__は標準規格のC99では__func__として定義されているもので、厳密にはマクロではないらしい.また、__VA_ARGS__の手前にある##はgccの独自機能で、これを付けていると、引数がない場合にfmtの後のコンマを除去してくれる.

これがないとコンマの後ろに引数がない様な状態になってしまうので、

$ gcc err_msg.c
err_msg.c: In function 'main':
err_msg.c:37:2: error: expected expression before ')' token

こんな感じにエラーになってしまう.

gccの独自拡張なのでclangでは##の部分でエラーとなる...はずだったんだけどコンパイルできてしまった.あれれ?

ちなみにclangのバージョンは、

$ clang -v
Ubuntu clang version 3.0-6ubuntu3 (tags/RELEASE_30/final) (based on LLVM 3.0)
Target: x86_64-pc-linux-gnu
Thread model: posix

少し前はエラーになったはずなんだけど、最近対応したのだろうか.

プリプロセス後の結果は以下のような感じ.

$ gcc -E err_msg.c
int
main(int argc, char **argv)
{
 const char msg[20] = "foo";
 err_msg("err_msg.c", __FUNCTION__, 37, "error", "hoge");
 err_msg("err_msg.c", __FUNCTION__, 38, "warnning", "fuga:%s", msg);

 return 0;
}

今時スマートな方法か?特にC++で.

もっとスマートな方法ってあるんだろうか?

後、C++的にはどうなんだろうか.

最近、CとC++の違い: プリプロセッサー編のような記事を見たり、Effective C++冒頭から、「もう#defineは辞めろ.」と書かれてあったりしてなんか良くない気がしてきている.

個人的に、最初はCで書いていたコードにbetter C的な範疇でC++のコードも中途半端に書き加えているので、もうスタイルが無茶苦茶.

そんなわけでC的にもC++的にもこういうエラーメッセージは今時どうしているか気になる.

*1: Success(0)が目障りだ.そういえばunpv12eではフラグでstrerror(errno)を出力する/しないを制御していた.ここからソースコードを入手できる.その中のerror.cにエラーメッセージ用の関数群が定義されている.