etolisp 進捗 (27) 〜 Object Systemの実装 (1)
【ニコニコ動画】創世のアクエリオンを聞きながら作業。「あっいーしーてーるー」がリフレインしてあまり捗らない。ニコニコ動画に「作業用BGM」タグがあるのを発見。ニコニコだと時間制限がないので,平気で「神曲メドレー90分」とかがアップされている。MP3をサイトに上げるのは違法だけど,FLVならおkという現状は,いかがなものだろう。始めから話がそれてしまった…軌道修正。
Paul Graham の本を復習してObject Systemの実装。etolispは,Lisp界のSQLiteを目指しているので,なるべく軽量に実装し,配布形態は1コのDLLになるように考えている*1。そのため,CLOSもどきもC++で小さくコツコツ実装する。
ClassとObjectを新しく,first class objectに追加。構造体の定義に関しては通常のsymbol-valueテーブルではなく,上書き禁止の特別なテーブルを作って,そこに置くことにした。defstruct,make-instance,slot-value, typep がちゃんと動くようになった。大きなトラブルも無く,構想通りに実装が終わった。細かいオプションにはまだ未対応。
ガーベージコレクタについて,コンストラクト中に GC が働いて,gcnew してポインタが帰ってきたときには既に死んでいる,というバグがあったことを,以前書いた。その時は,コンストラクタ内では GC::collect を呼び出さないバージョンの _etonew を使うという,チンケな方法で解決した。しかしながら,コンストラクタで呼び出す関数が全て GC::collect を呼び出さないように注意深く実装するというルールは,どう考えても,将来バグを生む危険がある。そこで,これを解決するために,GC::Sleep なるクラスを作り,GCを特定のスコープで眠らせる方式にすることにした。
いわゆる RAII idiom というやつである。
class Sleep { public: Sleep() { data_lock lock; ++s_sleep; } ~Sleep() { data_lock lock; --s_sleep; } };
みたいな感じで,s_sleep カウントが 0 より大きい場合は,GC::collect が働かないという仕組み。使い方は,GCを眠らせたいブロック中で
{
GC::Sleep zzz; //寝るでごんす
:
}
のように書く。zzz という変数名,なかなかよくない?
追記:本日の成果。
[kenobi]~/src/etolisp/src> ./etolisp etolisp> (defstruct rect width height) => t etolisp> (setf r (make-instance 'rect)) => #<Etolisp-Object (rect) 0x191d600> etolisp> (setf (rect-width r) 10) => 10 etolisp> (setf (rect-height r) 20) => 20 etolisp> (* (rect-width r) (rect-height r)) => 200 etolisp> (rect-p r) => t etolisp> (typep r 'rect) => t etolisp> (typep r 'integer) => nil etolisp> r => #<Etolisp-Object (rect) 0x191d600> etolisp>