■
さっきの続き。
eval時に行うmalloc(的な何か)は、eager型とlazy型を選べるようにする。
- eager型evalがさっき書いた、eval時に一括で申請して、領域が無くなったらエラー例外を投げるもの。
- lazy型evalは普通に、eval時には何もせずに、cons等の度にmalloc(的な何か)を行い、malloc(的な何か)が失敗した段階でエラー例外を投げるもの。
それぞれのevalが入れ子になった場合の挙動は、基本的に、外側のevalに準じるようにする。具体的には以下のようになる(文章にすると分かりにくいが、多分直感的だとは思う)。
- eager型evalの中でeager型evalが実行された場合、内側evalの最初に、外側evalが確保した領域の中から一括で申請し、そこから使っていく。
- lazy型evalの中でeager型evalが実行された場合、内側evalの最初に、外側evalのGC_MALLOCを通して一括でメモリを申請し、その中から使っていく。
- eager型evalの中でlazy型evalが実行された場合、内側のeval内でメモリが必要になった個々の段階で、外側のevalが確保したメモリ内から使っていく。
- lazy型evalの中でlazy型evalが実行された場合、内側のevalは、外側evalのGC_MALLOCを通して、個々にメモリを申請していく。
追加で、以下の特性を持たせる。
- どのevalでも、メモリがなくなった時はevalを終了し、「外側」にエラー例外を渡す。
- デフォルトでは、一番外側のevalはlazy型で動くものとする。
……という仕様が良さそうだ。
問題は、Gaucheでは、
/* Fundamental allocators */ #define SCM_MALLOC(size) GC_MALLOC(size) #define SCM_MALLOC_ATOMIC(size) GC_MALLOC_ATOMIC(size) #define SCM_NEW(type) ((type*)(SCM_MALLOC(sizeof(type)))) #define SCM_NEW_ARRAY(type, nelts) ((type*)(SCM_MALLOC(sizeof(type)*(nelts)))) #define SCM_NEW2(type, size) ((type)(SCM_MALLOC(size))) #define SCM_NEW_ATOMIC(type) ((type*)(SCM_MALLOC_ATOMIC(sizeof(type)))) #define SCM_NEW_ATOMIC_ARRAY(type, nelts) ((type*)(SCM_MALLOC_ATOMIC(sizeof(type)*(nelts)))) #define SCM_NEW_ATOMIC2(type, size) ((type)(SCM_MALLOC_ATOMIC(size)))
のように、直にBoehm GCのGC_MALLOCが設定されているので、
ここを直さない限りは、eager型の挙動にする事は出来ない。
しかし逆に見れば、ここで一旦マクロラッピングされているので、ここを直すだけで動く可能性はある、という事でもある。
とは言え、このメモリ領域の情報をどこに持たせるかと考えると、
- eval時に専用領域を用意する
- R5RS等でいう「環境」に持たせる
のどっちかだろうが、どちらにしても、単純にここだけいじって動くという事にはまずならないだろう……。
これを真面目に開発すべきかは、ちょっと悩む。
これを実装すれば、eval/svと一緒に使って、とりあえず「安全なeval」をGauche上で機能させる事が可能になる。
が……これ難易度ちょっと高くないか?