Let's start Scheme

2015-05-23

互換レイヤを書く

R6RS処理系の過半数(6個)についてFFIの互換レイヤを書いたので(Larcenyはまだテストが通らないけど)、今後のためにどういうことを気をつければいいかを記しておく。要するに愚痴である。

ドキュメントを当たる


まぁ、超基本である。ここだけで済むのであれば優秀なドキュメントを持っていると言える。互換レイヤを書くということは、言い換えると処理系依存の機能を同一APIに落とし込むということなので、まずはターゲットとしている処理系がほしい機能を備えているかを調べる。ここで見つけられなかったら諦めるという手もある。

ちなみに、今回ここだけで済んだ処理系はRacketとGuileのみである。この2つはドキュメントがかなり優秀といえる。(GuileはどうやってFFIのライブラリをロードするのかがドキュメントに書いてなかったけど・・・)

Schemeで書かれたソースを当たる


Schemeで書かれたソースは大体どこかに付属しているものである。たとえバイナリ配布されているものであったとしてもである(例:Sagittarius、Larceny)。明文化されていないとはいえ機能自体を持っているということは割りとよくある話で、そういう場合は今後後方互換性が損なわれる可能性があることを考慮にいれつつ書く必要がある。まぁ、変わったら泣く覚悟が必要という話である。

Schemeで書かれたソースも処理系によって大分違う。例えば、Mosh、Vicare、Sagittariusは基本的にライブラリ形式なので見つけてしまえばそのままライブラリを呼ぶことができる。Larcenyは多少違って、ライブラリ外の手続き・マクロは(primitives foo ...)のようにしてやる必要がある。これはvan Tonderのマクロで共通なのでNMoshもこの形式である。

今回ここまでで済んだ処理系はVicareであった。まぁ、VicareはFFIの機能的に多少物足りなかったのでなんとかならんかなぁとCソースまで覗いたが。

Cで書かれたソースを当たる


正直ここまで来たら諦めた方がいいレベルである。処理系によっては全くどこにも書かれていないが利用できる機能というものがある。具体的にはMoshのbytevector-pointerである。まともな感覚で書かれたものであれば、大体機能ごとにまとまっているものなので適当にファイル名からあさるという手がある。Mosh的にはFFIProcedures.cppという名前がずばりという感じであったので覗いたら見つけた。ファイル名から見つけるのはソースが一箇所に固まっているとやりやすいがそうではない場合もある(具体的にはLarceny)。そういう場合は以下のような古典的な方法を使ったりする。
find . -name '*.c' | xargs grep 'keyword'
探したいファイルの拡張子は適当に変える必要があるかもしれない。今回はFFI関連だったので、pointerとか
ffiとかがよく使われた。
Larcenyはソースがいろいろなところに配置されていたり、ドキュメントではなくREADMEにAPIの使い方が書いてあったり(具体的にはlib/Ffi/README)と手当たり次第当たる必要があった。

Schemeオブジェクトのメモリ配置に依存したコードを書く


ここまでやりだしたら正直いろいろ考え直した方がいいレベルである。Larcenyにはバイトベクタからポインタを作る手段がない。Vicareはバイトベクタをコピーする不便は振る舞いをするがそれでも存在したのであるが、Larcenyでは存在すら確認できなかった。偶然にもffi/handle->addressというものを発見したのでその振る舞いを確認。こいつは最終的にsrc/Rts/Sys/primitives.cに定義されているprimitive_object_to_addressが呼ばれることを確認し、この関数は与えられたオブジェクトからタグを取り除いたものを返すという振る舞いをすることが分かった。そうすると、バイトベクタの構造を知ってしまえばその要素がどのように配置されているかが分かる。いくつかのソースを覗いてbytevector_refというものが多用されているのを見つけて、その定義まで飛ぶ。include/Sys/macros.hで定義されているのだが、ここからLarcenyは構造体などという優男が使うようなものは使用しておらず、メモリのオフセットを直接弄るという男らしい方法をとっていたことを知る。最初の1ワードはサイズを格納して、残りは要素という感じになっているので、上記のScheme手続きで得られるアドレスにポインタのサイズを足した分をポインタとすることにした。

いい悪いは別にして、こんな超が付くレベルの危険な操作も可能にしているのは「何かしら打つ手がある」という希望があるので個人的には好きである。(この機能は必要だったのでSagittariusにもあったりはするが。)

まとめ


月並みな言葉になるが、ドキュメントは重要である。今回分かったのはユーザー視点で見ればRacketレベルのドキュメントはとてもありがたく、Mosh、Larcenyレベルのドキュメントは涙が出るということであった。VicareはなぜかFFIの項目だけやる気がないのでそれもどうかと思ったが。

後は、Chez、IronSchemeとYpsilonをやればR6RS処理系を網羅したライブラリになるのだが、正直もうやりたくないです感が自分の中に漂っている。

2 comments:

Shiro Kawai said...

ここに上がってる処理系はどうかわかりませんが、copying gc使ってる処理系だと、ポインタ使ってる間はオブジェクトを動かさないようにpinしておく必要がある場合も(Allegro CLはそうだった)。

kei said...

Larcenyはcopying gcな気がしますね。動かないようにできるか調べてみないと・・・

Post a Comment