例外をどうやって伝播させるか?
例外の実装について調べたり考えたりしていた。
一番多い内部実装はなんだと思いますか?
「うーん、ちょっと内部まではわからないなぁ」「何らかの方法でジャンプしてたりするのかな」
僕が調べた範囲では、setjmp、longjmpを使っている実装が多かった。
setjmp、longjmpの魔
setjmp、longjmpは気をつけてつかわないといけない関数だ。
setjmpは、関数を超えて、ジャンプできる。
関数から、他の関数へピヨーンとひっととび。
setjmp, longjmp とは
setjmp, lognjmp は古典的に標準ライブラリ関数として実装されているCライブラリ関数である。setjmp, longjmp はペアになって、関数の外にジャンプする機構を実現する。よろしいか、goto 文がたかが関数内部での制御の移動を実現するのに引き換え、この setjmp, longjmp は関数の間でのジャンプができるのである。だから、濫用すると、すぐに収拾がつかなくなるので、goto 文同様に禁止現場は多い。
Javaは安全に例外をつかえるのは、言語としてラッピングして、setjmp、longjmpの危険をなくしているからだ。内部実装は、かなり複雑になる。
戻り値で判定する
そもそも、例外なんていらないじゃん、戻り値で判定しちゃえば。こう考える言語もあります。C言語やgoだね。欠点は、戻り値にエラー判定を使っちゃたら、本来、求めたい値があった場合に、参照引数を渡さないといけない点。goだと多値返却だから、この点は問題がない。
int error = foo();
グローバル変数に設定
あっ、これはPerlね。グローバル変数の欠点は、すぐに保存しておかないと、ほかの処理によって、書き換えられてしまうのよ。例外をキャッチしたら、すぐにレキシカルに保存。これをきちんと書いておかないと。
my $error = $@;
SPVMにおける例外の実装は?
今、それを考えているところ。SPVMはPerlとCを簡単につなぐという目標があるんだ。Perlらしくもあり、Cらしくもある。
今考えていることは、次のこと。
例外処理をサポートする。例外が発生したかどうかは、戻り値で判定できる。本来必要な値は、スタックを使って返す。
int32_t SPVM__Foo__bar(SPVM_ENV* env, SPVM_VALUE* args) { (void)env; int64_t value = (int64_t)labs(args[0].lval); // 本来の戻り値は、第一引数に設定して返す args[0].lval = value; // 例外が発生したら1、そうでなければ0を返す return 0; }
これで例外処理をかけて、GCもうまくいくかどうかということを、今実装して試しているところなんだ。