移植にあたっての課題とその解決方法
ここでは、FSM/State.hとStateMachine.hをCLOS上に移植する上での課題とその回避策について考えます。
移植上の2つの課題
簡単に移植できると思うのですが、CLOSで扱うのが面倒臭そうな技術上の課題がいくつかあります。
上記のコードでは、以下のテクニックが使われています。
- テンプレート
- singletonパターン
こんな手法を使っている理由を考えてみます
テンプレートは、いちいちStateMachineおよびStateを継承したマーカクラスを作成するのが面倒くさいというのが主たる目的でしょう。
singletonパターンは、StateMachineを愚直に実装すると、StateMachineごとにStateのインスタンスを生成する実装が考えられます。しかし、これはメモリの使用効率が良くないです。StateMachineは1万とかオーダーで生成されることを想定されます。このたびにStateを生成していては無駄となります。なので、静的に確保したいのでしょう。
余談
singletonは、最初はnamespaceでも良いのではと考えてしまいましたが、これではダメですね。statemachineクラスが現在の状態を管理するには、右辺値として扱えるクラスであって欲しいですね。statemachineクラスがenter,execute,exit関数を関数ポインタで保持して、管理する形式にすれば可能ですが、それはあまりにも煩雑でしょう。また、テンプレートを使っていることを考えるとトリッキーなコードになりそうですし...。
テンプレートについての解決方法
これは困った。CLOSにはテンプレートとかないです。Common Lispなので動的型付けだし、みなかったことにしたいですねw
ありえない型への遷移も許容してしまう実装になってしまいそうですねぇ...。
マクロにして型チェックをするコードを挿入するというのも手ですが、これでも動的チェックになってしまいますね。
静的チェックを実装する方法については、今後の課題にしておきます。
singletonパターンについての解決方法
これまた困りました。どうしてこんなに世の中生きていくのが辛いのでしょうか?
以下の2つが制御できるかどうかが課題ですね。
- setf
- make-instance
CLOSでsingletonクラスの実装をしている人はいるようで以下のような実装がありました。
http://www.tfeb.org/programs/lisp/singleton-class.lisp
make-instanceを制御しているようです
(make-instance 'foo)
で毎回同じオブジェクトが返ってくるようになりますね。
(setf (make-instance 'foo) nil)
とかしたらオワタなきがします。
やってみました。
The function (SETF MAKE-INSTANCE) is undefined. [Condition of type UNDEFINED-FUNCTION] Restarts: 0: [RETRY] Retry SLIME REPL evaluation request. 1: [*ABORT] Return to SLIME's top level. 2: [TERMINATE-THREAD] Terminate this thread (#<THREAD "repl-thread" RUNNING {AB97279}>)
なるほどー。エラーがかえってきました。この実装でそのまま使えそうですねー。
わーい。
というわけで、singletonは大丈夫そうですね!