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; }
2009-02-09 (Mon)
2012-02-09 (Thu)
_ 100人のプロが選んだソフトウェア開発の名著 君のために選んだ1冊
100人のプロが選んだソフトウェア開発の名著 君のために選んだ1冊
前に書いた記事が載っている本がAmazonで予約できるようになったみたい。
「来年度からプログラムを書く仕事をすることになってしまった不幸なおまえらの道標となるべき書籍十冊」みたいなエントリは読むな。という人もいるけど、まだ読んでないのでこの本にもあてはまるのかどうかよくわからない。
実は、紹介する本として最初に思い付いたのはThe Craft of Text Editing―手作りのテキストエディタだったんだけど、やはり絶版だったので、さすがに紹介するのはどうかなと思ってやめておいたのであった。 The Craft of Text Editingはエディタの本なのだが、多くの本と異なり、エディタを使う人のためではなく、作る人のために書かれている。 これを読んで、いつかRubyでエディタを作ってみたいと思いつつ、早十数年…ほら、やっぱり年寄の昔話になってしまった。
以下のサイトで英語版のPDFやEPUBを入手できるので、「NHK出版 ラジオ基礎英語1」の後で読んでみたら面白いかもしれない。
_ Rubyのfor式の失敗
前に書こうと思って忘れてたのだけど、shiroさんの記事を読んで思い出した。
Rubyにはfor式というものがあって、最近だとあまり好まれていないようだが、それはたぶん主に見た目の問題で、eachに対するたんなる構文糖だと思われている(ので直接eachを使った方がシンプルだと思われている)せいではないかと思われる。 しかし実は、Rubyのfor式はたんなる構文糖よりも少し罪が重い。それは、for式は変数のスコープを導入しない、つまり、ループ変数の破壊的な変更を前提にしている構文だということだ。
shiroさんの昔の記事の例でいうと、
irb(main):001:0> a = [] => [] irb(main):002:0> for i in 0..4 do a.push(lambda { i }) end => 0..4 irb(main):003:0> a => [#<Proc:0x2265df20@(irb):2 (lambda)>, #<Proc:0x2265ded0@(irb):2 (lambda)>, #<Proc:0x2265dea8@(irb):2 (lambda)>, #<Proc:0x2265de80@(irb):2 (lambda)>, #<Proc:0x2265de58@(irb):2 (lambda)>] irb(main):004:0> a.map {|i| i.call} => [4, 4, 4, 4, 4]
のようにlambdaでクローズしたつもりの変数の値がすべて最後の値に変更されてしまう。 eachを直接使うと、ブロックパラメータは毎回の呼び出し毎に別の変数になるので、こういう心配はない。
irb(main):005:0> a = [] => [] irb(main):006:0> (0..4).each do |i| a.push(lambda { i }) end => 0..4 irb(main):007:0> a => [#<Proc:0x2270c124@(irb):6 (lambda)>, #<Proc:0x2270c0fc@(irb):6 (lambda)>, #<Proc:0x2270c0d4@(irb):6 (lambda)>, #<Proc:0x2270c0ac@(irb):6 (lambda)>, #<Proc:0x2270be18@(irb):6 (lambda)>] irb(main):008:0> a.map {|i| i.call} => [0, 1, 2, 3, 4]
C#でも今から直すそうなので、(もっとユーザが少ないと思われる)Rubyでもたんなるeachの構文糖にしてしまうとか、Scala風のfor式にしてしまったらどうだろうか。
_ moris [この方法でソース中に埋め込んだ全デバックを、一括で無効にしたい場合にいい方法はありますか?]