Cマクロの恐さ
本日はCマクロの恐さについて思う存分味わってしまいました。
マクロ地獄
まずは次のコードを見ていただこう。
void Scm_CallContinuation(ScmObj cont, ScmObj ret) { ほげほげー。 if (frame != INVALID_CONTINUATION_OPAQUE && CONTINUATIONP(continuation_stack_unwind(cont))) { ほげほげー。 } ほげほげー。 }
おかしい事に気づいたのは、gdbでcontinuation_stack_unwindにbreakpointを仕掛けてみた時。何故かこの関数が2回呼ばれている。コードからは絶対に一回しか呼ばれるはずがないからだ。(数時間経過)。色々悩んだあげく、なんとなく gcc -E してみた。すると驚くべき事を発見した。
void Scm_CallContinuation(ScmObj cont, ScmObj ret) { ふがふがー。 if (frame != ((void *)0) && (((((unsigned int)(continuation_stack_unwind(cont))) & (0x3 << 1)) == (0x2 << 1)) && ((((unsigned int)(((((ScmObj)(((unsigned int)( (continuation_stack_unwind(cont)))) & ((~0U << (2 + 1)) & ~(0x1 << 0)))))->cdr))) & (0x1 | (0x3 << 1) | (0x7 << 3))) == (0x1 | (0x3 << 1) | (0 x3 << 3)))) ) { ふがふがー。 } ふがふがー。 }
bit の魔術は目に入れない頂きたい。ここで注目すべきは、 continuation_stack_unwind が現れている”回数”である。2回現れてますね、はい...。これによって余計な副作用が生まれるプログラムも当然有る。呼出側からすれば絶対に1回しか呼ばれないという暗黙の仮定を作ってしまうので、思わぬバグとなってしまう。
Cマクロを使用する上で予期せぬ関数の呼び出しを防ぐための手段とかって確立されているんでしょうか。他にもこういう使い方をしている所が絶対に有るので、マクロ側で対処したいです。今回のこの件でもっとCの知識をつけなければならないと実感しやした。こういう手痛いバグには二度と突き当たりたくないし。
# バグを作りこまないプログラミングスタイルをもっと確立せねば...修行足りねえなぁ...うぅ...関数型だったらこんな事無いのにな...
SigScheme開発録 (51)
- SCM_OBJ_COMPACT (GCはまだ)
- 上記問題を回避。test-exp.scm が core を吐くが、test-char, test-string, test-srfi34等はほぼパス。
- test-exp.scmは以下のコードで落ちる。学校から帰ってきたら調べる。そういや学校先週の水曜日以来か...。
(if (provided? "sigscheme")
(begin
;; causes error when evaled
(assert-error "case invalid form #6"
(lambda ()
(case 1
((1) . 2))))))