空想犬猫記

※当日記では、犬も猫も空想も扱っておりません。(旧・エト記)

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> 

*1:SQLiteはソースファイルも1コなんだよね…すごいね!