Gauche:FeatureIdentifier
Shiro: 2007/03/29 02:19:39 PDT
システムによって有ったり無かったりする機能を、ユーザプログラムにどうやってハンドルさせるか、という話。要はCで書くときの
#if defined(HAVE_FOO)
... fooを使ったコード ...
#else
... 代替コード
#endif
みたいなことをSchemeで出来るようにしたい。0.8.10に入れたいところだが、間に合わなければ
もう少し後になるかも。
基本はcond-expand
基本方針として、こういう切り替えは全てcond-expandでやりたい。(GaucheRefj:cond-expand)
(if (global-variable-bound? ...) ...) 等の方法は、
- テストは実行時に行われるため、コンパイル時に解釈されるフォーム (use等) を切り替えられない
- 本来、ifやcondの中にトップレベル定義は書けない (今のGaucheは書けてしまうがそれはバグ)
という欠点があるため。
cond-expandを使うとなると、特定の機能があることを示すfeature-identifierを
どう管理するか、ということを考えなければならない。特に、ユーザ提供の拡張モジュールに
ついて、柔軟かつロバストにfeature-identifierをシステムに追加してゆく方法が
必要である。
要求仕様。
- feature-identifierは、対象となるコードがシステムにロードされる前に
見えてなければならない。例えば特定のfeatureがあるならそれを提供するモジュールを
useしたい、という場合。あるいは拡張モジュール自身をコンパイルしている最中に
cond-expandで定義されるものを切り替えたい場合。
拡張モジュールのロード時にfeature-identifier
を追加する方法ではうまくいかない。
- Gauche本体のコンパイル限定の問題だが、コンパイルに使われるgosh内で定義されている
featureと、コンパイルの対象となるgoshが持つであろうfeatureが異なる場合がある。
(例えば、pthread無しでコンパイル・インストールしたgoshを使ってpthread有りの
goshをコンパイルする場合。) このことから、feature setをgaucheバイナリの
外部から指定することが可能でなければならない。
- central repositoryは避けたい。例えばSLIBはslibcatという単一ファイルに
(requireで使う)feature symbolとパスのalistを保存しているが、
ユーザに自由に拡張パッケージを追加/削除させようとすると、
ひとつのファイルをインストール・アンインストールの度に触らなければならない。
特にアンインストールでミスって一貫性が失われるのが嫌だ。それに、
システム標準ではないディレクトリにユーザ権限でインストールされた拡張モジュールを
どうするかって問題もある。
- 毎回ライブラリディレクトリをスキャンしたくない。central repositoryが無いとすれば
拡張パッケージ毎にfeature setを記述したファイルをインストールしてゆくことになるが、
gosh実行時にそれらをスキャンするのは(特にload-pathが長い場合)結構なオーバヘッドとなる
可能性がある。それは避けたい。スキャンした結果をどこかにキャッシュしておく
というのも、キャッシュの一貫性保持が面倒なのでやりたくない。
- 機種依存のバイナリファイルと、機種非依存のschemeファイルは別々の箇所に
インストールされている。最近は昔ほど多くないだろうが、異機種がNFSで
ディレクトリツリーを共有しているような場合に、後者を共有できるように
するためだ。ここで、feature setは機種依存である可能性があるので、
(たとえそれがテキストファイルであっても)前者の方にインストールする
必要がある。
- アンインストールは関連ファイルを消すだけ (いざとなったら手動でも可) という
ふうにしておきたいんで、feature set指定ファイルはモジュール本体からなるべく
見つけやすい位置に置いておきたい。
- cond-expandは複数の処理系で使われるコードで、処理系依存部分を
切り替えるのにも使われる (このためにGaucheではgaucheというfeature identifier
を定義している)。他の処理系のfeature identifierの衝突はなるべく避けたいので、
gaucheで始まるプレフィックスを一貫して採用したい。
方針。
コンパイル時だけ別のfeature setを使う
Windows/VC++版で、*.scm -> *.c のprecompilationをクロスでやる必要が
出てきたのでなんとかしないとならなくなった。
とりあえず拡張パッケージのことは考えず、メインのGaucheのfeature setは
コンパイルインされているものとする。
- 現在はグローバルに(シングルトンで)持っているfeature setをオブジェクト化
- feature setをコピーして、必要なfeatureを足したり引いたりする操作をつける
- compileの環境としてfeature setを渡せるようにする。
- cond-expandの展開はcompileセッションに渡されたfeature setを参照するようにする。
最初のふたつは難しくない。後のふたつはどうするのが良いだろうか。
- feature setをコンパイル時環境に加える (現在のところ、
コンパイル時環境は module(グローバル環境) + レキシカル環境)
- parameterで持ち回る。
- feature identifierをsyntacticな束縛としてグローバル環境に加える。
(「syntacticな束縛」はdefineやlambdaなどコンパイラが認識する束縛ってこと)。
1, 2は通常の束縛とは直交する名前空間を持つことになるので
あんまり綺麗じゃない (現状もそうだけど)。
3は名前空間のルールが統一されてすっきりするけど、うっかりユーザモジュール内で
feature identifierと同名のグローバル変数を定義してしまうと
元のfeature identiferがシャドウされてしまうとかいう落とし穴があるかも。
(R6RS的に、束縛の衝突を許さないことにすれば安全なんだけど、
影響がでかいしなあ)。
一番簡単なのは2.かなあ。
Last modified : 2007/10/07 06:35:56 UTC