Perlテックブログ

ITエンジニアの成長意欲を刺激する技術考察、モジュール開発の日記。Perlイベントや国内や海外のPerlの記事の紹介。

例外をどうやって伝播させるか?

例外の実装について調べたり考えたりしていた。

一番多い内部実装はなんだと思いますか?

「うーん、ちょっと内部まではわからないなぁ」「何らかの方法でジャンプしてたりするのかな」

僕が調べた範囲では、setjmp、longjmpを使っている実装が多かった。

Java, C++, rubyなど。

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らしくもある。

今考えていることは、次のこと。

例外処理をサポートする。例外が発生したかどうかは、戻り値で判定できる。本来必要な値は、スタックを使って返す。

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もうまくいくかどうかということを、今実装して試しているところなんだ。

XSとは異なって、SPVMのCバインディングは、本当にただのC言語関数なんだ。