シンボルを足す手法

マージを前提にすると、何らかの形でmodularityのある内蔵関数インターフェースが必要になる。
つまり、socketが有るホストと無いホスト、cairoが有るホストと無いホストではインタプリタが内蔵する定義が異なるようにしなければならない。しかし、内蔵定義を変更するのは大変な(ビルド時にSchemeインタプリタが必要になる)ため、内蔵定義を変更するのではなく、ライブラリレベルで吸収する手法(手法3)にした。

  • 手法0 : free-varsを修正する×

コンパイル時に埋め込まれるfree-varsを修正し、また、別途CProcedure objectを生成するコードを挿入する。
もちろんこれは動作するが、残念ながらSchemeには#ifdefが無いし、このリストから生成されるコードが存在するためコンパイル時にSchemeインタプリタ(Gauche)が必要になる。
PerlRubyがやっているように、Boot strapのためのインタプリタのビルドを許容できるならこれは選択肢になりうるが、どっちにせよGaucheへの依存ができることになる。

  • 手法1 : FFIを使う△

実は現段階ではFFIを使うのが一番確実に動作する。つまり、Scheme Objectの生成処理は本当のSchemeで書き(これらはFFIでリンクされるプログラムからは操作できない)、必要な.soなり.DLLへのリンクをShared Objectに任せる。
しかし、FFIはそもそもそういう目的のために作られたものでないし、この手法ではFFI非対応のホストでは拡張機能は使えなくなる。

  • 手法2 : setValueString×

VMはsetValueStringインターフェースを公開している。要するに、symbolと値の対応を外部から操作でき、実際、コマンドライン引数等の引渡し等に利用されている。
これは一見上手く行きそうに見えるが、上手く行かない。
もし、moshをセーフモード、つまり-5オプション付きで起動したなら、setValueStringで加えた値をスクリプトから参照することができる。しかし、R6RSモードでは上手く行かない。
R6RSモードでは、コンパイル時に組み込まれたコードが一旦Schemeソースを展開し、それを実行する。その組み込みコードが"知っている"シンボルのリストはやはりfree-varsであり、要するにこの手法はfree-varsを修正するのと同じだけの手間になる。

  • 手法3 : "Internal" Function Interfaceを作る○

結局のところ、内部の関数をFFIのように呼べるようにするのが一番ストレートな解法に思える。内部関数は引数の変換を必要としない点だけが外部関数と異なる。
この手法なら、Scheme側とC++側の両方でScheme objectを取り扱えるので、必要な前処理をSchemeで書くかC++で書くかを選べるというメリットが有る。