まだ,制御された解体(controlled demolition)の実験段階だが,反応は悪くない.アプリ終了時にTREEVIEW::EraseTreeViewで描画オブジェクトの無差別削除が実行され,PAIRBOXのデストラクタが呼び出されると,PAIRBOXでは自ノードの所属するPAIRLISTを割り出して(もし,存在すれば)PAIRLIST::dataCountDownを呼び出す.これは自分自身の死亡通知を管理者に送るのと同義で,管理者はそれに従って(埋葬などの)事後処理を実施することができる.
これまでもCleanSansyoなど同等趣旨の関数は実装されてきているが,今回はっきりと,これが手続き(処理)ではなくコミュニケーションの一種であるということに気付いた.デストラクタからの呼び出しは謂わば死者からのコールバックに相当する.自分ですべてをやろうとするより,それを知っている人に依頼した方が早いし,確実だ.自分の葬儀を生前にデザインすることは可能だが,実施することはできない.つまり,必要なのはプロシージャからプロトコルへの転換だ.
▲ZTシステム構成図7.ZELを基準ノード=202 NODULEで開いて終了するとき,PAIRLIST::dataCountDownでリスト上にオブジェクトが見つからないというエラーが発生する
dataCountDownの呼び出しはPAIRBOXのデストラクタ→Disposeから実行される.ノード対リストを管理しているPAIRLISTを割り出すためにはPAIRBOXがPAIRLISTに接続していなくてはならない.ということは,このノードはリスト中に含まれているのにfind関数で見つけられなかったということになる.⇒PAIRLIST::findは直列リストの部分しか見ていない.PAIRLISTはそれから分岐した「端点共有ノード対リスト」を持っているので,そちらも探さなくてはならない.もし,findという関数の仕様が元々「直列リスト要素を探す」ことであるのなら,別の探索関数が必要になる.⇒findは以下から呼び出されている.
- CheckShiftedPairBox(bool force) (TOPOLOGY)
- RetrieveGhost(void) (NAMEBOX)
- dataCountDown(PAIRBOX * pbox) (PAIRLIST)
- putbottom(PAIRBOX * pbox, PAIRBOX * common) (PAIRLIST)
いずれもfindの動作を拡張しても問題ないように思われるので修正しみよう.⇒いや,現行でもPBOX::findは端点共有リストまで探索するようになっている.PAIRLIST::nextでそれをやっている.⇒いや,それどころではなくなった.とんでもないことが起きている.nodule:grandparentの実行カウントがテストのたびに変動している.あり得ない!これは決定性チューリングマシンだ.仮にマルチスレッドで並列実行されるようなことがあったとしても,止める場所は同じなのだから実行回数が変動するはずがない.
nodule::grandparentの引数オブジェクトのsnumが225749のときダンプするように仕掛けて変動を見たところ,画面が出力されるまでは一定だが,終了ボックスをチェックして最初にダンプされる時点でカウント40くらいの変動がある.信じ難い現象だ.何が影響しているのだろう?画面が出てから最初のダンプまでの間にgrandparentは3万回近く実行されている.ccid=’&’ COUPLINGでダンプしてみよう.どうもこの関数は画面を表示してアイドリングしている間にも呼び出しが掛かってくるようだ.⇒カーソルが系図画面に入っただけで呼び出しが掛かってくる.
getUpperClassNodeというgrandparentを呼び出しているだけの関数があるので使ってみよう.getUpperClassNodeは画面を開くまでに37058回呼び出されているが,アイドリング状態では変動しない.OCXではOnMouseMoveを取っているので,ここからDLLの関数が呼び出されているのだろう.どんな関数が呼び出されているのか知りたいが,また後でということにして,PAIRLIST::findに戻ろう.
PAIRPLIST::DumpPairListでもこのノードはダンプされない.PAIRLIST:nextboxが間違っている.というか,bottomに間違った値が入っている.nextboxは現在要素がbottomのときは次要素としてNULLを返している.bottomは誤っている可能性が高いので生値を返した方がよい.いや,この関数ではなくLIST::nextだ.⇒修正しておこう.⇒LIST::nextの中でbottomの整合性チェックを行うようにしたところ,この不正がかなり早期に発生していることが検出された.
MakePairListClean…→ RetrieveGhost…→ PAIRBOX::Dispose→ PAIRLIST::CleanSansyoで起きているようだ.PAIRBOXはLIST::_removeで削除されている.⇒いや,これは過渡的なものではないか?⇒いや,違うかもしれない.bottomlistの操作をdeleteの後に実行するようにしたが,同じだ.LIST::_removeの作動中フラグOnLISTremoveを立てて,この間はチェックしないようにした.
この後,bottomlist, topolistの不整合が続いたので,LIST::_removeで行っているパラメータ更新をすべてLIST::_removeに移動した.ただし,これは例外的な措置でリスト要素がPAIRBOXである場合に限定される.これでPAIRBOXの削除に関わる不整合は完全に一掃された.
▲TREEVIEW::EraseTreeView→~TRIBEBOX→…SIMPLEGRAPH:TakeRemainSansyo→…LIST::nextでbottomlist不整合が発生した
これは全く上記と同じ事象と考えられるので,対策も同じだ.EDGELISTはNLISTの派生クラスなのでLISTの孫クラスに当たる.SIMPLEEDGEのデストラクタでdataCountDownを発行し,それをLISTで受けるようにすればよいはずだ.NLISTは追加パラメータを持たないので,すべてLISTで処理できる.いまのところdataCountDownはPAIRLISTのメンバ関数になっているが,これをLISTに移管してしまった方がよい.まず,それをやっておこう.⇒対処した.
SIMPLEEDGE::Disposeでは始点・終点オブジェクトへの参照解除しかやっていない.getgraphという関数はあるが,所属リストの取り出し関数もない.ここでは簡略にDisposeの中で直接LISTを探して,そこにdataCountDownを送ることにしよう.ただし,getUpperClassNodeではそのオブジェクトに埋め込まれたCIDを探しにゆくので,LISTを直接探しても見つからない可能性がある.というより,見つからないだろう.pclassid(基本クラスIDテーブル)を使って親クラスを探すことができたはずだ.NODULE::IsSuperClassという関数がある.これを使ってgetUpperClassNodeを拡張すればよい.⇒誤動作する可能性はないだろうか?可能性はゼロではないが,まずないと考えてよいのでは?
求めるクラスIDをCIDとしたとき,CIDをそのものずばり持っているオブジェクトがあればそれを優先しなくてはならない.その手前にスーパークラスにCIDをもつ別のクラスオブジェクトがあった場合には問題になるかもしれない…安全策としては,まず現行方式で探索し,発見できなかった場合には拡張探索するというのが順当なのではないだろうか?とりあえず,そのように実装してみよう.拡張探索には現行探索も含まれているので多少冗長にはなるが,仕方ない.⇒実装した.
▲アプリを起動して,TRIBELIST::TribeRelocationのステージ【7.2】完全木検定:すべての系列を完全系列として正準化するでMakePairList…→ TOPOLOGY::CheckPairList… →GENEBOX:CheckInverseCycle…→ LIST::cancel…→ ~SIMPLEEDGE→ Dispose→ LIST:dataCountDownでdetacountが負になった.CheckInverseCycleでグラフを初期化するためにEDGELISTをキャンセルしているところだ.LIST→NLIST→EDGELISTの間のどこかでdatacountをデクリメントしているのだろう.
いや,違う.LIST::cancelではDATALIST::cancelが実行される.削除は自前でやっているが,cancelは下請けに出している.LISTからDATALISTに通知を送るか?ないし,DATALISTを含む大改造に着手するか?⇒これはかなり難しい問題だ.
- リスト要素のデストラクタでは自分の死因を割り出すことはできない
- dataCountDownはリスト要素の属するLISTの派生クラスのインスタンスに送られる これはXLISTとする
- XLIST→LISTではリスト要素のクラスによってパラメータの更新を実施
- 処理がLISTの内部で完結している場合はこれでよいが,基本クラスのDATALISTに処理を依頼している場合には,DATALISTに死亡通知を送らなくてはならない
- DATALISTではdataCountDownを受け取ったときにはパラメータ更新を実施しない
いや,もう少し簡単な方法がある.DATALISTに何かフラグを設ければよい.たとえば,blackflagとしておこう.LISTではdataCountDownを受け取ったときにはBFを立てればよい.DATALISTではBFが立っているときにはパラメータの更新を実施しないでフラグを落とすだけとする.LISTでたとえば_removeなどの処理を行った場合には_removeで落とせばよい.これでよいのではないだろうか?
リスト要素をdeleteする前にフラグをリセットしておけば,まず間違いはないと思われる.外部で削除された場合にはどうなるか?LISTではDCDを受け取ったタイミングでBF+するが,これをリセットするものはいない.従って,deleteを実行する前のリセットは必須である.
現行のdataCountDownにはまだ不備がある.この関数ではdatacount,bottomlist, curnodを更新しているが,toplilstは更新されていない.本来なら,datacountとtoplistの更新はDATALISTで実施しなくてはならないところだ.ただし,toplistはリスト先頭ノードの接続スロットなので,特に何もしなくても更新された状態にはなっている.つまり,現状でも問題はない.⇒修正は入れ終わったが,エラーが出ている.
▲アプリ終了でEraseTreeView→~TRIBEBOX→… SIMPLEGRAPH:TakeRemainSansyoを実行中にLIST::nextでエラーが検出される.