過去の修正をFIXするという取り組み

過去の修正をFIXするという取り組みは進んではいるが,進捗は捗々しいものではない.あまり手が掛かるので一括変換で#ifdef を #if defined に変換したのが却って裏目に出ている.キーワード(コンパイルオプション)の定義・未定義を判別するのが著しく困難になってしまった.こうなったら振り出しに戻ってすべての#if definedを1個づつ点検してゆくしかないのだが,その前に正規表現の構文を研究して,#define文で(値を設定しない)定義だけのキーワードを選り分けることができるようにしておきたい.後ろにコメントが付いていない文は検索できるが,コメント付き定義文だけを抽出することができない.

昔はSEDやVIを使ってバッチファイルで多数ファイルの一括変換などを自在にやっていたのだが,一度あるところで社内LANの大元のHD(システム全体をカバーするタワー型のストレージ,当時のことでわずか70MB,ユーザの端末にはHDは搭載されていなかった)のユーザファイルを全部消してしまうという大失策をやったことがあり,それからは足が遠のいてしまった.(このときはHPのサポートチームが飛んできて鮮やかに復活せてくれたのだが…大いに懲りた)

できた!これでコメントありの定義文をすべて抽出できる

#define\s+\S*\s*/ (A)

157個あった.#define文は修正FIXの問題があるので集中管理した方がよいと思う.ただし,そのファイル内に限定したローカルなデバッグ用オプションは従来通り認めることにする.ローカルなオプションは(デバッグ時にしか使われないのだから)#if defined(_DEBUG)のブロックに置かなくてはならない.#define文は原則として以下の4つのファイルのいずれかに置くことにする.

  1. nodule.h 修正履歴,関数の論理・仕様に関わる定義(INCOMPLETE,PENDING)
  2. comdebug.h バージョン管理に関わる定義,ライセンスに関わる定義,デバッグ用ツールに関わる定義 (VERSION)
  3. Bobjecct.h デバッグ用オプション定義(TEMPORARY),検証に関わる定義(VERIFICATION)
  4. coupling.h テスト環境に関わる定義(ENVIRONMENT)

まず,この観点から定義文を整理してみよう.たとえば次の例文を見てみよう.

#define SOLVETRIBESPLIT // 系列間スプリット検定の動作を従来仕様に戻す

もし,SOLVETRIBESPLITがどこからも参照されていなければ,この定義は無条件で「廃止」される.いまの場合,このキーワードは別の場所で別の意味「系列間スプリットの発生により移動が実施された」として使われていた.つまり,本来の用途してはフィックスしていることになるが,そうでない場合にはnodule.hのINCOMPLETEかないしPENDINGカテゴリに移されなければならない.もし,この修正が正当であることが検証されれば,定義を廃止して修正履歴に移動する.ただし,単純に「廃止」されただけの場合は必ずしも「履歴」として保存されなくてもよい.いまの場合は「単純な廃止」に相当する.

検証用にこの論理を(場合によっては加工して)残す必要がある場合には,nodule.hのVERIFICATIONに移動する.また,事後の再発に備えてデバッグ用に論理を残す場合にはBobject.hのTEMPORARYに預けることになる.昨日コメントなし定義の抽出用に使っていた #define (?!.*( |\t)).*$ で検索するとヒット件数が7しかない.ほとんどの定義の後ろにコメントを付けたこともあるが,付けていないものもある.

#define\s+\S*\s*$ (B)

これで30件ヒットした.多分これは動作していると思う.(A)式と(B)式を合体して1式にできるだろうか?

#define\s+\S*\s*/
#define\s+\S*\s*$

これは特に難しくなさそうだ.末尾が/か$で終わればよいのだから…つまり,

#define\s+\S*\s*(/|$) (C)

でよいはずだ.これで186件検出した.もう一度個別にテストしてみよう.前者が158件,後者が30件で単純合計は188件となる.ということは重複が2件存在しなくてはならないのだが…エディタで並び替えができると簡単に重複行を調べることができるのだが…常用しているエディタのMeryではマクロでその機能が提供されているはずだが,見つからない.もう一度インストールし直してみよう.⇒今度はProgram FilesのMeryの中にMacrosというフォルダができた.早速使ってみよう.

確かにダブっているエントリが2つある.

#define    _POLLFILE_H_
#define    __HOSTCMD_

テキストに「/」が含まれる行を探す(A)式を実行しても上の2つが検索結果に入ってしまう.このタイプの記述はすべてのヘッダファイルが持っているので,この2つだけ特異な動作になるというのはおかしい.(A)式を実行して「/」を含まない行が検出されるということはあり得ないのでVSの「検索」のバグと考えるしかない.(C)式を使えばもちろんこのようなことは起こらない.⇒すべてのオプション定義にコメントを付けて以下のコードで識別できるようにした.

  1. INCOMPLETE: nodule.h 0
  2. PENDING: nodule.h 28
  3. COMDEBUG: comdebug.h 25
  4. TEST: coupling.h 17
  5. VERIFY: Bobject.h 47
  6. DEBUG: Bobject.h 35
  7. LOCAL: *.cpp 7
  8. 合計 6種 *.h 5本, *.cpp 6本 159

オプション定義のある*.cppファイルは,SimpleGraph.cpp, Potential.cpp, PairBox.cpp, MargBox.cpp,Jikusenzu.cpp,DoublyBlessed.cppがある.たとえば,「LOCAL:」を含む定義を検索するときには以下のような式を使った.

(#define\s+\S*\s*(/|$))(.* LOCAL:)

INCOMPLETEはいまのところゼロだが,検証結果によってはPENDINGから移行してくることもあり得る. Bobject.hに含まれるVERIFYとDEBUGは完全にニュートラルでなくてはならず,実行フローやロジックに影響を与えないことが保証されなくてはならないが,現状ではその確証はない.PENDINGに含まれる項目のほとんどは直ちにフィックスしてもほぼ問題ないと考えられるが,ここでは一旦置いて,先に「無定義オプション」つまり,「負論理で定義された仮修正」の始末を急ぐことにしよう.これを行う方法としては,とりあえず,#if defined/!defined文を虱潰しにチェックするしかないような気がする.ともかくそれをやってみよう.

VERIFICATIONとDEBUGの相違点は前者は一般に常時走っているのに対し,DEBUGはバグをトレースする目的で一時的に出動するというところだ.この意味ではASSERTIONなどはVERIFICATIONのカテゴリに属するとも言える.というか,VERIFICATIONとして扱われているロジックは将来的にはASSERTIONに昇格するものと見るべきだろう.VERIFICATIONにはデバッグ支援という目的で設置されているので,(DEBUG_NEVERなどと同様)必ずしもリリース版で動作することを意図していない.つまり,ASSERTIONとVERIFICATIONの違いはリリース版に常設されるものであるか否かという点にある.VERIFICATIONでは事象が発生した時点で停止するだけでよいが,ASSERTIONにはアプリを続行するための例外処理が整備されなくてはならない.

▲comdebug.hで定義するASSERTIONマクロに例外処理を整備する

「MAXKEISANGOSAを適用する@20171229」は未定義だが,論理が逆(負論理)になっている.この論理は「再開発スタート版」でもそのようになっている.つまり,本来は#ifndefでなくてはならないところが,#ifdefになっていたのではないかと思われる.「MAXKEISANGOSAを適用」に関係する論理は無数にあるが,オプションになっているところでは.「MAXKEISANGOSAを適用しない」という流れになっている.しかし,必ずしも普遍的にそうなっている訳ではないので,どうも統一的な方針を欠いたまま場当たりに修正しているのではないか?という懸念がある.ここでは,「MAXKEISANGOSAを適用する@20171229」に#define文を与えた上で,現行論理のまま据え置くことにしておくが,全般的な見直しが必要と考える.

いや,違う.もっと重大な失敗を冒している.やはり,機械的に#ifdef から#if definedに置換した失敗は大間違いだった.定義されている場合はそれでよいが,未定義の場合は例外なく負論理で書いているので真偽を逆にしなくてはならない.手動で修正していたときにはそのような論理の書き換えを行っていたのだが…機械的に置換したところより,前まで戻るしかない.とんでもないことになってしまった.

一括変換をやったのは昨日のことだ.昨日は2回バックアップを取っている他にDLLフォルダだけのバックアップを一度やっている.

  1. 始業時バックアップ 10:07
  2. 一回目バックアップ 13:53
  3. DLLをバックアップ  15:32
  4. 二回目バックアップ 19:06 

おそらく一回目バックアップは一括変換を実施する前に実行されていると思う.(でなかったらアホだ)⇒一回目バックアップではまだ,prohibitlistとshiftlistの始末も付けていない.DLLをバックアップ でも同じだ.二回目バックアップでも手を付けていない…#ifdef は379点まで減少している.多分「#ifdef _DEBUG, _DEBUG_, XDEBUGの3種をすべて#if defined()に一括変換」という辺りだろう.

prohibitlistとshiftlistのパージはログの冒頭近くで書いているが,実際には修正のFIXと並行して,その片手間にやっていたのだろう.いずれにしても二回目バックアップは安全なところまでしか一括変換をやっていないので,ここからスタートすれば十分安全と思われる.すべてをここまで巻き戻すのではなく,*.hに関わる修正は現在のものが使えるのではないかと思う.重要な書き換えはすべてヘッダファイルに入っているので,これを再利用できれば相当な利得がある.まず,そういう形で設(しつら)えてみよう.かなり手戻りだが,失敗するよりはましだ.

系列枠のシフト管理用ノード対リストを廃止

#ifdef プラグマを潰す作業を続けているところだが,まだ840個も残っている.今日中に終わるだろうか?動作には影響のない益の少ない作業のようにも思われるが,これが完了すればソースコードがかなり読み易くなることは確実なのでがんばってみることにしよう.幸いVSは検索結果を表示するウィンドウを3つ持っているので,残存する#ifdefの検索結果を「検索結果テーブル」に表示しながら,別のウィンドウでキーワード検索するなどのことが並行して実行できるのは大変都合がよい.

ノード対は通常基本世代枠リスト→世代枠→ノード対リストに接続されている.系列枠は世代シフト管理用ノード対リストというのを2種類持っている.一般にリストはリスト要素オブジェクトをスロットゼロを使ってチェーン状にリンクしている.逆に言えば,リスト要素は一つのリストにしか接続できない.系列枠が持っているノード対リストはどうやってノード対にアクセスしているのだろう?

系列枠のprohibitlistとshiftlistは現在使われていないように思われる.⇒終了時にcancelするところはあるが,リストを構成している論理は見当たらない.すでに廃れているのではないだろうか?⇒TRIBEBOX::CleanSansyoでは対象オブジェクトがこの2種のノード対リストである場合は停止するような論理になっている.つまり,明らかにこの2つはとっくの昔に廃止されている.⇒パージは簡単に終わったが,実行時エラーが出た.⇒クラス構成の始末がついていなかった.TRIBEsPROHIBITとTRIBEsSHIFTが残っていた.

この修正に伴ってNAMEBOX::shiftnum,NAMEBOX::targetnumも不用になるのではないか?TRIBEBOX::RemoveFromShiftListも不用になる.UpdateShiftList,ShiftMultiCards,RemoveFromShiftList,DumpShiftList,DumpProhibitListも同様.もしかすると不用ロジックはもっと広範囲に拡がる可能性もある.そもそも,カードシフトという概念自体がすでに崩れているのではないだろうか?もしかするとこれは事実かも知れないが,影響が大き過ぎるので後日ということにして先に進む.OBSOLETEで仮止めしてある部分が廃棄されることは確実なので#ifdefを#if definedに書き換える必要はないと思われるが,OBOSOLETEではあまりに一般的過ぎるので「系列枠のシフト管理用ノード対リストを廃止@20201109」に変えておこう.

▲InitializePrinterはすでに使われていないのではないか

#ifdef _DEBUG, _DEBUG_, XDEBUGの3種をすべて#if defined()に一括変換して#ifdef は362個まで減少した.あとは個別にこつこつと修正してゆくしかない.⇒VSの検索・置換は正規表現が使えるので試してみよう.以下で一括変換できた.

検索:#ifdef (.+)\b
置換:#if defined($1)

キーワードの中に全角@が入っていても処理できる.ただし,この置換は行末に閉じ括弧が入るので行中にコメントが入っているとコンパイルエラーになる.コメントが入っているのは1ファイル1個くらいなのですぐにビルドできるようになった.あとは,存続するものと廃止するものを切り分けるだけだ.#define文は1210個もある.定義だけで値を持たないリテラルをピックアップできればよいのだが…以下の検索式で定義だけのリテラルを検索できる.

検索:#define (?!.*( |\t)).*$

ただし,後ろにコメントが付いているものはこの式では弾かれてしまう.mmm…少しおかしい.49個しかヒットしない.尻尾に何か付いているものがあるのだろうか?⇒コメントが付いているものと思われる.これは別途扱うことにする.

ともかくまず,この49個を始末してしまおう.いや,これらは基本的には問題ない.というか検証する必要はあるにしても原則としては存続というのがルールだ.始末しなくてはならないのはむしろ「無定義」のキーワードだ.それをどうやって見つけることができるだろう?#ifdef から#if definedに書き換える手順の中でそれを一つづつやってきたのだが,一括変換してしまったので境界線が消えてしまった…あとは,#if definedを一つづつチェックするしかないのではないだろうか

残っている分岐はすべて現状でフィックス

過去の修正をすべてフィックスして完全にさらの状態から再出発するための工程を進めているところだ.肝要な部分に関してはすでにほぼ完了しているが,#ifdef, #ifndefによる条件コンパイルの分岐はまだ1000箇所以上ある.できるところまでは整理しておきたいが,基本的に現在残っている分岐はすべて「現状でフィックス」という方針なので作業的には難しいものではない.分岐には#defineでキーワードを定義したものと,それを欠いているものがある.#defineがないものはその場でフィックスしてよいが,#defineがあるものについては検証が必要だ.

分岐を残す場合にはその目的・用途によって以下の4種に区分することにする.①TEMPORARY,②INCOMPLETE,③PENDING,④VERIFICATION.TEMPORARYはデバッグ時のダンプなど必要に応じて一時的に用いるもの,INCOMPLETEは修正が確定していないもの,PENDINGは再検討を要するもの,VERIFICATIONは動作確認のために(常時/一時的に)設置するものとする.

キーワードに関わる分岐が複数ファイルに関係する場合にはヘッダファイルを使った広域に適用される定義が必要だが,1ファイル内に留まる場合にはソースファイルの冒頭に直接記述する略式を認める.キーワードには原則日本語でその趣旨を明示するようにする.フィックスしたキーワードは原則としてコメントとしてファイルに残すこと,修正箇所は少なくとも@日付でマーキングして修正履歴をトレースための手がかりを残しておくこととする.つまり,@日付はタイムスタンプであると同時に識別子でもある.キーワードにはコメントで解説を付することが望ましい.日付だけのキーワードは望ましくない.また,日付が変わっても一連の修正には同一キーワードを適用する方がよい.

分岐を残す場合にはすべて#if definedないし#if !definedの形式に改める.ただし,一時的な仮修正では#ifdef 形式で負論理の条件式を用いることを認める.(逆に言えば,#ifdef形式の修正はすべて「仮修正」と認定される)暫定的な修正では#define文を切った上で,PENDINGとすべきである.PAIRLISTでdatacountの残留が発生するという事象があるので,先に見ておくことにしよう.

PAIRLIST::~PAIRLISTでdatacountの残留が発生している.アプリを起動→終了で再現できる.⇒これはノーマルな動作である.delete PAIRLIST→~PAIRLIST→dispose→cancelで始めてdatacountはゼロになる.check文の中で検査すればよいのだが,呼び出し頻度の高い関数は余分なものをできるだけ持たないようにしているので,速度最優先オフのときはcheckブロックが使えるようにしておこう.

同上サンプルで終了時,世代に_INT_MAX(FARFUTURE)が入っているPAIRLISTがある.~PAIRLISTのscount=76と77,SNUMは307と308だ.PAIRLISTのダンプで表示される世代番号は親の世代枠から取っている.世代枠が空であるためだ.⇒系列枠は世代シフト管理用ノード対リストというのを2種類持っている.シフトノード対リストと禁止ノード対リストだ.これらは管理用のノード対リストであり,直接系列枠に接続して,世代枠には繋がっていない.⇒系列枠に接続しているPAIRLISTの場合はそれを明示的にダンプするようにした.PAIRBOX::dumpのASSERTIONには誤りがあった.

また失敗した.

1>d:\zelkova\zelkovadll\src\mergecard.cpp(878): fatal error C1020: 予期しない #endif です。

多分これは孤立した#endifを削除するだけで通るだろう.⇒うまくいったようだ.RECYCLENEWNUMBER(リサイクル時には新しい通番を付与する)は現状でフィックスすることにする.すべての系列枠を親参照パスから切断する@20180503も確定しておこう.この他,確定した主なオプションはあとでリストにして示すことにする.これらの修正履歴は原則nodule.hに記載することにする.

#ifndefは完全に片付いた.以下は除外した.①マイクロソフトのコード,②ヘッダファイル冒頭のヘッダ名の宣言,③_DEBUGなどデバッグモードを示すキーワード.#ifndefにはこれらによって73件が残っている.#ifdefの現在数は846だ.ともかく,片付くまでやるしかない.一度バックアップを取っておこう.

コンパイルオプションの真偽を負論理から正論理に変更する

過去のすべての修正をフィックスし,まっさらなところから「再開発スタート版」を開始するという方針を立てて作業を続けている.fixという英語は「修理する」という意味に使われるが,ここでは「修正が完了し,バージョンの分岐が存在しなくなった状態」と定義する.つまり,#if, #else, #endif がソースコード上から消えることを意味する.nodule.h, Bobject.h で定義されていた条件コンパイル式に関してはすべてFIX完了し,「仮修正」,「暫定」の文字列はシステム内から完全に一掃された状態になったが.,分岐を持つ修正がまだかなりの個数残っている.これらはTEMPORARY, INCOMPLETE,および保留※で識別できるようにしてある.※はPENDINGで識別することにした.

TEMPORARYは一時的な修正で元のソースに戻すという前提で挿入されているもの,INCOMPLETEはまだ修正が確定していないとみなされるものという位置付けだ.これらが,その定義通りの状態になっているかどうかを試すには,#defineで定義された識別子の真・偽を切り替えてみればよい.TEMPORARYは一時的なコードなのだから,オンでもオフでも正常に動作しなくてはならない.従って,TEMPORARYプラグマがこの条件を満たしていれば,リリース版では復旧させるという条件下で存続が認められる.INCOMPLETEの場合も,同様に識別子をオン・オフして動作を確認することで正当性をチェックできる.もし,オンで不具合が出ればオフの論理が正しく,オフで停止すればオンの論理で確定できる.いずれでも不具合が発現しないとすれば(実際はほとんどそのようになっていると思う),その修正の意味を読んでメリットのある方を選択することになる.オンとオフの動作が同じなら,場合によってはTEMPORARYに移管するということも考えられる.

一つ問題なのは現状ではこれらのプラグマのほとんどが負論理になっているという点だ.これは#define文を省略して,識別子の意味と反対の論理で記述されているということを意味する.つまり,たとえば「この文は誤り@20201107」が未定義のまま使われている #ifdef この文は誤り@20201107 #else #endif のブロックではアクティブなブロックが誤りで,アクティブでないブロックが正しいという逆の意味になる.

#ifdef この文は誤り@20201107 (#defineしていないので偽)
  アクティブでないブロック(この文は正しい)
#else
  アクテイブなブロック(この文は誤り)
#endif

オン・オフの切り替えテストを行うためにはかなり不都合なので,とりあえず,すべてのプラグマで#define文を宣言して正論理に切り替えておく必要がある.まず,この修正をやっておこう.修正が必要なところは60箇所くらいある.ことのついでにこのような場所では#ifdef ではなく #if defined() を使うようにすることにしよう.こうしておけばより詳細な条件式を論理演算子を使って記述することもできるし,通常の#ifdefとも区別することができるだろう.

現在システム上には#ifdef文が893個存在する.#if文は69個あるが,うちVSが生成する*.rcの4個を除く65個はすべてVBの#If文(大文字のI)だ.いや,まだある.#ifndef が178箇所で使われている.#ifdefと合わせると1071個の条件コンパイル式があるということになる.これを総点検するというのは現実的ではない.ここでは#defineを置いていない条件式は「一掃」の対象になると宣言するに留めておこう.

▲「保留」されている論理を点検しフィックスする必要がある

ともかく,TEMPORARYとINCOMPLETEに含まれる項目を正論理に変えるところから始めよう.

▲終了時PAIRLISTのデストラクタでdetacount 非ゼロが起こり,ダンプが表示される.サンプルはゼルコバの木モジュール構成図 TEST.ZEL,基準ノードは#33 baselist,軸線図法.datacount=2.

▲#ifdef _DEBUG_で実行を抑制しているブロック/関数が135箇所ある.この中にはすでに廃れてしまっているロジックなども含まれると思われるが,一度点検する必要がある.

#ifdef OBSOLETE(廃れた)はNOTHINGと同様,無条件に削除してよい.いや,待てよ.何かおかしなことが起きている.OBSOLETEの中に含まれているGetOSDisplayStringという関数はそれ以外のどこにも含まれていないのに未定義にならない.⇒いや,出てきた.未解決の外部シンボルとなっている.つまり,この関数はOBSOLETEにはできない.あるいは完全に廃止するかのどちらかだ.このコードは確か外部から拾ってきたものでOSのバージョンなどを取得するためのルーチンだ.残しておいてもよいのではないか?⇒if !defined(GetOSDisplayStringを停止@20190109)で使えるようにしておいた.

この関数を廃止した理由はこのコードがWindows 7 あたりまでしかサポートしていないためだ.修正すれば,Windows 10でも使えるようになるかもしれないが,’GetVersionExA’: が非推奨 というエラー(警告)が出てビルドできないので差し替えが必要だ.警告をエラーとして扱うというオプションを緩和すれば使えないことはないが…このようなこともあるのでいきなりOBSOLETEを削除もリスクがある.⇒OBSOLETE 3件を削除した.NOTHINGは6件ある.⇒始末した.

OCXで使われているint TEMPORARYの意味・用途が不明.「プレビュー→ノーマル一時切り替え中」となっているが,値はゼロのまま変化していない.⇒廃止でよいと思う.TEMPORARYはすべて整理した.INCOMPLETEに移ろう.INCOMPLETE@20190128はdecidePrimaryNodeを隠蔽しているが#elseは空で,改訂版のdecidePrimaryNodeは無条件で動いている.これはすでに確定しているものと判断する.INCOMPLETE@20190129には複数の用途がある.

  1. decidePrimaryNodeで(primarynode->getmarglink() != getOyalink())のとき停止しない
  2. SetMinorTribeで「基準ノードを参照する系列の優先ノード切り替えは禁止」を無視する

多分どちらも反例があると思われるので検証が必要だ.INCOMPLETE@20190202はもっといろいろな場合を含んでいる.

  1. EstablishMajorTribeChainで「系列順位が逆転している場合」を無視
  2. MakeUpTreeで先祖ノードが単身先祖配偶者のとき,EstablishMajorTribeChain(先行系列との接続関係を確立する)を実行しない
  3. TRIBEBOX::MakeUpTreeのループでSetPrimeNode(系列優先仮ノードと優先実ノードを設定)を実行する
  4. MakeUpTreeで「系列優先ノードが隠蔽されている」とき停止しない
  5. SetupPrimaryNodeで始系列の場合も「系列優先実ノードが家内婚配偶者で隠蔽されている場合」の探索を実施する
  6. SetupPrimaryNodeで始系列でない場合には優先実ノードが隠蔽されている場合を認める

これはおそらく完全に確定した修正のように思われる.INCOMPLETE@20190204を見てみよう.

  1. TRIBELIST::MakeUpTreeでEstablishMajorTribeChain(先行系列との接続関係を確立する)を実行する代わりにSetupPrimaryNode(系列優先仮ノードと優先実ノードを設定)している
  2. setMajortribeでsetrealnode(系列優先実ノード参照をリセット)しない
  3. GetMajorTribeChainでIsSolidNameBox検査とsetrealnodeを実行している
  4. TRIBEBOX::GetRealnodeで系列優先実ノードが見つからないとき,優先仮ノードのNameBoxから取り出している処理を廃止

これも確定しているように思われるが,項目4が正当であるためには,前段で系列優先仮ノードから必ず優先実ノードを決定できることが示されなくてはならない.INCOMPLETE@20190130は2箇所だけだ.

  1. ReduceMultiCardで人名枠をIsVisibleCardではなくIsValidNameBoxで判定している
  2. ReduceMultiCardで仮ノード消去の対象カードから配偶者を除外している

項目2は配偶者が消去されるのはBTWの場合だけということを主張している.これは正しいのではないか?最後のINCOMPLETE@20190131は6箇所だ.

  1. TRIBELIST::MakeUpTreeで系列優先仮ノードが隠蔽されているとき,優先仮ノードを探索してTRIBELIST::MakeUpTree(系列優先仮ノードと優先実ノードを設定)するブロックを廃止している.ただし,#elseブロックは空なのでこれは確定と考えるしかない.SetupPrimaryNodeには「隠蔽された系列優先ノードの補正」という処理が入っているので,おそらくここでは不用になったものと思われる.
  2. CARDLINK::getProxyで系列優先仮ノードを無条件で可視化している.いや,違う.これは一般の人名ノードの場合だ.結婚リンクを与えて,その結婚リンクに繋がる仮ノードを返すという関数だ.ここで可視化する必要があるのかどうか?という点に関しては疑問がある.
  3. TRIBEBOX::SetPrimeNodeでSetupPrimaryBoxの代わりにsetPrimenodeを呼び出している.SetupPrimaryBoxという関数はすでに廃止されているので,これは確定だ.
  4. TRIBEBOX::GetRealnodeで系列優先実ノードが有効なカードでなかった場合の処理が挿入されている.#elseにはこれを代替するようなコードは含まれていないので,これで確定とするしかない.
  5. Bobject::getvisibleで(hidden() && visible && PHASE >= INITIALIZED)のとき停止するのを抑制している.hiddenかつvisibleという状態はあり得ないとしているのか?無視してよいと見ているのか,意図が不明なので暫定的に検査を復活させておく.

項目2で隠蔽ノードまで無差別に可視化しているところを見ると,この修正ではカードが隠蔽状態にあることと可視であることは無関係と考えているように思われる.実際,隠蔽状態にあるノードが表示されないというのは事実だがそれは可視状態であるか否かということとは無関係だ.従って,項目2も確定と見てよいのではないだろうか?そうすれば,INCOMPLETE@20190131のプラグマは完全に消える.まあ,一応残して様子を見ることにしよう.まず,TEMPORARYのカテゴリに属するオプションを全部止めて動作を確認しておこう.これらは止めた状態がノーマルな状態のはずだから,当然動かなくてはならない.

GetOSDisplayStringで「’GetVersionExA’: が非推奨として宣言されました。」が出る.「GetOSDisplayStringの使用停止@20190109」がTEMPORARYに入っている.⇒これはPENDING というカテゴリを作って移しておこう.

上の1件を除く4件のTEMPORARYオプションを止めて走らせたところ,Bobject::getvisibleで停止した.これは上記INCOMPLETE@20190131の項目5で「意図が不明なので暫定的に検査を復活させておく」とした論理に該当する.これは現行論理で確定でよいと思われるが,気になるのでどんなノードが引っかかっているのかを見ておこう.printnamaeでダンプしようとしたが,この関数からgetvisibleを呼び出しているのでスタックオーバーフローが発生してしまう.printshortなら大丈夫だろう…⇒#279 kakeizu(0)だ.PHASEは12でMAKEUPTREE.系図木を構築しているところだ.Yリストへの繋ぎ込みはこのフェーズで実行されるので,このフェーズでは無視としてみよう.⇒これで一応描画できた.

完全木テストを通してみよう.全体図が通れば親族図は大体通るので全体図テストでもよかったのかもしれないが,サンプルが小さいのでそんなには掛からないだろう…この版は多分完全被参照リスト管理が動いているのでかなり遅い…それでも1分以内に終わった.添付サンプルも問題ないようだ.次にINCOMPLETEのカテゴリのオプションをすべて止めて動作を見ることにしよう.いや,この辺りで一度バックアップを取っておいた方がよいかもしれない…次はINCOMPLETEオプションをすべて止めて動作を見ることにする.

TRIBEBOX::GetAlternativePrimeNodeで停止した.サンプルは渋沢一族8.ZEL.基準ノードは#211 大川平兵衛の息子.このエラーはMakeUpTree→EstablishMajorTribeChainの中で起きているものだ.forループの中で (T2 == this) のとき,(oyalink)で停止する.これにおそらく一番近いのはINCOMPLETE@20190202なのでこのオプションを復活させて走らせてみよう.⇒+INCOMPLETE@20190131で動作

TRIBEBOX::GetRealnodeにかなりひどいバグがあった.これはオプションとはまったく無関係の独立のバグだ.realodeが空のときにrealnode->getrelationを呼び出そうとしている.考えられないミスだ.ここには「実ノードが単親婚保持ノードの配偶者の場合:配偶者の配偶者を実ノードとする」という説明が付いている.こんな初歩的なバグが残っているとは…⇒このバグが発現しなかったということはこのフローを踏まなかったということなので廃棄しておこう.⇒解決

INCOMPLETE@20190131を追加してこの図面は出力できた.渋沢の全体図テストをやってみよう.いや,それどころではない.下のパネルを出した状態でハングしてしまった.

image

再現しなくなった…⇒表示が出るまでに時間が掛かっているのではないか?通常はOKボタンで直ちにパネルを閉じるが,何かのタイミングで閉じるのが遅くなったための現象ではないか?渋沢で描画までに8.5秒掛かっている.PAIRLISTのダンプを出しているためかもしれない…

INCOMPLETE@20190131単独では同じエラーが発現する.INCOMPLETE@20190131と@20190202がセットとなる必要があるので,この2つは確定ということにしておこう.@20190131の修正は例の隠蔽ノード可視に関わるものなので,この論理が動作に不可欠であることが分かる.@20190202は未決が7個もあるので一発で確定でよいのかどうか不安もあるが,一連の修正として一括確定とする.

INCOMPLETE@20190129の修正は2箇所で,一つはASSERT_NEVER(primarynode->getmarglink() != getOyalink())の停止,もう一つは「優先仮ノードが変化しない場合,系列に多重カードが存在する場合は切り替えを許可する」という緩和策を廃止して一律に基準ノード参照系列をリセットするものだが,いずれもオリジナルを復活させておくことにする.(オリジナルの場合,条件が成立するときは停止するようになっている)

INCOMPLETE@20190130は確定でよいと思われる.また,この確定により,IsVisibleCardは使用されないことが確定する.INCOMPLETE@20190204の修正の主眼はEstablishMajorTribeChainの廃止ではないかと思う.その代わりに導入されたのが,SetupPrimaryNodeだろう.EstablishMajorTribeChainの呼び出しを行わないというのはすでに@20190202で導入されていてそれを徹底したのがINCOMPLETE@20190204ということになるのではないか?どの道,その方向に進んでいるのだから徹底した方がよい.

これでINCOMPLETEはすべてフィックスされた.これらのオプションは2019年1月28日から2月4日までの比較的短い期間に集中している.つまり,一貫した一つのテーマの下に進められたものと思われる.

作業開始時点で893個あった#ifdefは873個になった.わずか20個しか減っていないが,後の残りは原則すべて現状でFIXでよいのではないかと思う.日本語で定義されている分だけでも確定してしまいたいのだが,どのくらい時間が掛かるだろうか?⇒意外にそれほどの時間は掛からないものかもしれないが…プラグマが一つのファイルに限定される場合にはヘッダファイルを使わずにCPPだけで始末ができるだろう.そのやり方ならあまり時間は掛からないと思う.

すべての修正をフィックスし「再開発スタート版」のインタクトな開始点を確保する

サブマシンにログインしようとしたらパスワードが間違っています,になった.何度打ち直しても通らない.電源を落として再起動したが,変わらない.仕方がないのでキーボードを外してタブレットでスクリーンキーボードを使って入力したら通った.キーボードをつなぎエディタを起動してテキストを入力してみると,”ai”と入力したつもりが,”a5”となっている.NUMキーがオンになっていた.いつもと違ってキーボードの右上にあるインジケータ3つのうち2つが点灯していることには気付いていたが,文字というより刻印のアイコンが小さくて読めない.ともかくログインできたらネットでマニュアルを探して調べてみようと思っていたが,ログインできないのだからどうすることもできなかった.

NUMキーをオンにするためにはFnキーを押しながらInsキーを押さなくてはならない.そんな込み入ったことを間違ってもやるはずはないと思うのだが,そうなっていたのだからいつの間にかそんなことをしていたのだろう.昨夜は意識はかなりはっきりしていたのでまだまだ作業できそうな気分だったが,思っているよりずっと意識が希薄になっていたのかもしれない.昨日は仕掛りの修正をいくつかFIXしておこうと思ったのだが,何度も惨めな失敗を繰り返している.

クリティカルな修正を行う場合には,まず共通ヘッダファイルで「SIMPLEEDGE_generationを廃止@20201020」のようなリテラルを定義し,それから#ifdef #else #endifで修正前のコードを保存したまま修正する.Visual Studioは偽になっているプラグマのブロックをグレー表示してくれるので,FIXするときにはグレーのブロックを削除すればよいというだけの話だが,#if #endifがネストしているような場合もあり,そういうときにはすべてグレーで塗りつぶされてしまうので見間違えるリスクはかなり高くなる.実際問題として100箇所修正を入れるより,修正点が100箇所ある修正Aをフィックスする方が難しい.

今日はもちろん全天快晴でクリアできるとは思うが,やってみなければ分からない.対策としては失敗に備えて細かくバックアップを取るしかないが,パッケージ全体をバックアップするのではなくDLLのソースとヘッダに限定すればかなり簡単になるだろう.

昨日の目標は「いくつかの修正をFIXする」ことだったが,今日は「すべての修正をFIXする」ところまで拡張しようと思う.このセッションを「再開発スタート版」というテーマで開始した以上,まず,それに相応しい開始ポジションを確保しなくてはならない.#defineで定義された条件コンパイルによる修正の他に,「仮修正」ないし「暫定修正」のコメント付き修正が無数にある.これらを一掃して完全にクリアな状態までソースコードを戻すというのが今日の目標だ.作業に掛かる前の版を別に保存しておくことにしよう.

とは言え,STOP文の実行を一時的に抑制するために入れた暫定修正などもある.⇒これらは一度通常の状態に戻してから改めて逐次止めるなどの措置を取り直すことにする.このような暫定修正が2本ある.暫定修正@20201026と暫定修正@20201028だ.⇒一応現行のサンプルではどこにも停止しない.あとはグレー表示のブロックを一掃するだけだ.ただし,日付だけはコメントに残して置いた方がよい.日付があれば後日ログを逆引きすることも可能だ.いや,これでも結構失敗する.大きいブロックでは修正するたびにコンパイルして失敗したらUNDOで戻るというのしかない.VSはプラグマブロックをーで閉じると「アクティブな/でないプリプロセッサブロック」を表示してくれる.これに従うのが一番確かだ.小さい+ーのチェックボックスを使うので,マウスが滑らかに動かないと作業にならない.⇒大体要領がつかめた.

ようやく調子に乗ってきたと思ったら,

fatal error C1004: 予期せぬ EOF が検出されました。

が出てしまった.UNDOでどこまで戻ればよいか?⇒一つ戻っただけで収まった!つねにプラグマ(ディレクティブ)を3つ削除しなくてはならないのだが,最後に除去する#elseを取り忘れていたのだろう?これでソースファイル1本が終わった.SimpleGraph.cppには元のコードが見えなくなるくらい修正が入っていた.ビルドしてみよう.ビルドも通る.実行も可能だ.⇒if (0)などのコードがある.暫定修正で処分が決まらない場合は,「保留」のリテラルを使ってコードを残しておく.「NOTHING」は無条件削除.Jikusenzu.cppが終わった.いや,終わっていない.Bobject.hにもかなりのコンパイルオプションがある.これらも一旦すべてFIXしてしまうしかないだろう.もし,後日問題が発生したら,「ZELKOVA 2020-11-06 最終版」まで戻って調べ直すしかない.⇒ともかく先にnodule.hのオプションに集中しよう.

ここでバックアップを取っておこう.ZelkovaDLLフォルダの中にはINCLUDEとSRCがあるが,フォルダごとバックアップすることにする.370MB,コピーは数秒で終わる.⇒どうも大失敗をやらかしたような気がする.「SIMPLEGRAPHのattributeを廃止@20201105」を止めたままで作業を開始してしまった.全部一からやり直さなくてはならない.⇒この修正は一旦放棄することにしよう.DrawingObjectを実装するときにもう一度見直せばよい.それほど大きな修正ではない.残っている部分は「保留」にしておけばよい.

「有効結婚リンク判定の重複処理削減@20201031」をFIXして実行時エラーが出てしまった.修正に失敗している可能性が高いが…UNDOでそこまで戻せるかどうか?やってみよう.関係するファイルは,KinshipDegree.cpp,PartialMap.cpp,TableSort.cppの3本だけだ.ファイルを比較してしまった方が早いだろう.⇒余分なことをしていた.FilteringKinshipを実行後でなければ意味がない検査を前方に移動していた.その場の思い付きが命取り…なんでそんなことをやったかって?見た目にその方がかっこいいと思ったから,そばにあった大文字のASSERT_NEVER3つを寄せて並べてみただけ…

「SIMPLEGRAPHのattributeを廃止@20201105」はやっぱり復活させておかないと不都合だ.すでにこの修正を当てにしているところがある…⇒いや,それも少しおかしいのでは…止めた状態でこれまでビルドできていたのだから,どこかが変わってしまったと見るしかない.未定義エラーが出ているところで修正を誤ったのではないか?⇒確かにそのようだ.バックアップから復元した.⇒フルバックアップしておこう.⇒また修正を誤っているようだ.未定義が出てきた.DataList.cpp.DATALIST::cancelでBottomlistを呼び出している.⇒修正した.

nodule.hの分が完了した.17本の条件コンパイル付き修正をFIXした.他にテスト用暫定修正が3件.次はBobject.hで定義されているものを見てみよう.ここではまとめてバックアップしておこう.⇒Bobjectの分が完了した.14件をFIXした.うち4件は廃止.その他に保留としたものが2件.「ファイルへ出力をサポートしない@20180314」とCOLLAPTIBLEMARRIAGE(結婚枠を折り畳む機能をサポートする)だが,よくわからないので保留としておいた.DLLだけバックアップ.

仮修正が41件,暫定修正が35件,そのほかに暫定を含む文字列が26件ある.暫定と仮修正では仮修正の方が一時的なものという度合いが強いので,まずこれからチェックしてみよう.いや,こちらは後回しにした方がよい.仮修正の中にはテスト用に設置してそのまま放置というのもあり得る.暫定修正は一応論理的には意味のある修正と考えられるから,それが安定動作しているという意味で外せるかもしれない.

暫定修正の場合は「暫定」を取って「修正」とし,日付だけ残すことにしよう.削除されている場合は「x行削除」とする.暫定修正で一時的に設置しているものはTEMPORARY@yyyymmdd のように表記することにしよう.⇒暫定の文字列はソリューション全体から一掃された.一時的な動作に関してはすべてTEMPORARYの語を含むようにした.暫定公開,暫定リリースなどは非公式版公開,非公式リリース,応急対処版などとした.現在「TEMPORARY」の出現は28箇所ある.

▲LoadWndPostの修正にあいまいな点があるので確認を要する.

仮修正@20190202が8箇所もある.これは調べる必要がある.修正が閉じていない可能性があるものはINCOMPLETEを付することにする.失敗した.手順を誤って仮修正@20190202をすべて仮修正に変えてしまった.⇒バックアップから復元した.仮修正@20190131も12箇所ある.これもINCOMPLETEに付け替えておく.⇒仮修正はほとんど同じ日付で複数存在する.@20190204が4個,@20190128が3個,@20190130が3個,@20190129が2個.あとはFIXできそうだ.INCOMPLETEは32箇所になった.

  • TEMPORARY 28
  • INCOMPLETE 32

TEMPORARYはあくまで一時的なものなのでそれほど問題性はないが,INCOMPLETE はFIXする必要がある.

「完全被参照リスト管理」の実装を完了した

「完全被参照リスト管理」という仕組みを実装しているところだ.これが完成すれば,込み入った参照関係の網目が完全にアンダーコントロールに入ったことになる.この処理の追加コストが時間的・空間的にどのくらいのものになるのかが興味あるところだが,まだバグが取り切れていないのでもうしばらくかかりそうだ.ファイルを開くまでは順調に動作しているが,系統並び替えを実行するとPAIRLIST::cancelで被参照カウントの残留が発生する.何が残っているかは新設した完全参照リストをダンプするだけで確定できた.DATALISTクラスで参照を発生させないための改修を行っているのでその修正がまだ収束していない.

再現手順は構成図 TEST.zelをUNDOCHAINで開いてgraph3でソートだけだが,#43490 PAIRBOXで被参照カウントの残留が起きる.暫定的にBottomlist(NULL)の行をリスト要素の削除の前に実行するようにして解消した.この修正が正当なものであることはあとから見ることにしよう.引き続き同種のエラーが今度はTRIBELISTのcancelで発生する.ただし,このエラーの原因は明瞭だ.TRIBELIST::cancelの中からDATALIST::cancelを呼び出しているためだ.これをNLIST::cancelないしLIST::cancelとすれば問題は解決する.⇒解消した.PAIRLIST:cancelのループでリスト要素を削除する前にBottomlist(NULL)で要素への参照を解除することは妥当でありかつ必要である.

このサンプルでざっと包括テストを通してみよう.

上記サンプルを#29 PDBでソートして「逆転循環サイクルの枝を破棄」が発生し,SIMPLEEDGE::dumpでlinkにPAIRBOXが入っているために停止した.GENEBOX::CheckInverseCycleでは検査に枝グラフgraph1を使っている.このグラフの節点はPAIRBOXだ.グラフを作成するときにそのグラフのノードの型を設定できるようにしておくとよいのではないか?graph1は逆転循環検査にしか使っていないので名前も明示的なものにした方がよい.⇒InvertCycleGraphと呼ぶことにした.

SIMPLEGRAPHにはattributeという変数を持っているが,モノ/ヒモノの判別以外の用途には使われていないので,この領域に属性ではなくノードクラスのCIDを格納するとよいのではないか?そのグラフが何を扱っているかを特定することは有用だし,CIDが分かればモノ/ヒモノを判別することもできる.モノはCIDを持っているが,ヒモノの場合はたとえば-1などの値を入れればよい.もし,ヒモノもデータタイプごとに識別したいのであれば,負値の定数を与えればよい.⇒改造してみよう.デフォルトはnoduleタイプとし,原則つねにグラフのnew関数ではコンストラクタに引数を与えて呼び出すことにする.⇒この修正は一段落してから実施することにして,ここでは停止しないようにしておこう.

完全木テストを実施してみる.全体図テストと親族図テストを統合したものだ.⇒どうもさすがに描画が遅くなっているような気がする.これではリリース版には採用できないかもしれない.つまり,デバッグ用途にしか使えないということになる.まぁ,それはそれでそれなりの有用性はあると思われるが…現在のサンプルで計測したパフォーマンスは

TIME=0.193 validcardnum=66 validmargnum=24 nodecount=2967 lastsnum=64006  INTRASH=204 REUSE=60835 WASTE=61039 NEWOBJ=64006

のような数値だ.カード66,結婚24で系統並び替えに要する時間は0.2秒,アクティl部なオブジェクトは2967個,ゴミ箱に204個あり,生成されたオブジェクト総数(再利用を含む)は64006だ.これを完全参照リスト管理導入前と比較してみよう.一旦ここでバックアップを取っておく.⇒旧版に戻して実行してみた.

TIME=0.084 validcardnum=66 validmargnum=24 nodecount=580 lastsnum=805  INTRASH=72 REUSE=153 WASTE=225 NEWOBJ=805

この差は隠しようがない.時間効率で2.3倍,空間効率で5倍,ステップ数で80倍の差がある.これだけ大きな差があるとさすがに公開版に適用するのは無理があるのではないだろうか?完全参照リスト管理の有用性は後処理にあるが,最大の後処理はアプリ終了時の一回しか実施されないし,そのときにはユーザはそこでどれほど時間が掛かってもほとんど気にしないだろう.(この後処理は完全参照リストを使ってもっと効率化することはできるが,実効的にはほとんど有意性を認められない)

ロジックが分岐するのはメンテナンス上好ましくない.ロジック的には前者の方が整理されているし,後者は前者の特定ケースとして処理可能なので,前者を採用することでフィックスし,ReferenceControlだけをNOPにすることで対応するということにしよう.残念だが,仕方ない.ReferenceControlを止めると数字は以下のように変わる.

TIME=0.084 validcardnum=66 validmargnum=24 nodecount=580 lastsnum=805  INTRASH=72 REUSE=153 WASTE=225 NEWOBJ=805

導入前と完全に一致している.まぁ,これでよいのではないだろうか?これを現実として受け入れるしかないだろう.ところで,この改修(完全被参照リスト管理の導入)に取り組むことになったきっかけは何だったのだろう?きっかけは「抽象グラフ検証系の複線化」という修正の中で非参照カウントの残留が発生したという辺りのようだ.シューティングのために従来方式の参照リストを使おうとしたら不具合が起きたりしたためだろう.ことのついでに「NODULE,Bobject,DATALISTなどを「抽象クラス」として再定義する」というのをやってみよう.

クラスを抽象クラス化するには仮想関数の少なくとも1個を純仮想関数にする必要がある.まず,DATALISTクラスを見てみよう.ここには仮想関数は一つしかない.この関数は実装されているが,実際には何もしないでゼロを返しているだけなのでこれを純粋仮想関数とするのは容易だ.「DATALISTクラスの抽象クラス化@20201105」で修正してみよう.⇒実装したが,DATALISTのインスタンスを生成しているところがある.さっきまで掛かっていたReferenceControlだ.DATALISTの抽象化を止めるか,新たに派生クラスを作るしかない.

この参照リストを作るためにNODEREFという構造体を新設しているが,その代わりに参照リストクラスというクラスを新設するというのがもっとも正しい.REFLISTというリテラルはすでに使われているので,NODEREFLISTとしてみよう.クラスを追加登録するためには,まず,nodule::getClassNameにCIDとクラス名文字列を登録しなくてはならない.以下のように登録した.

case ‘x’: s = “NODEREFLIST”; break;    // < ‘h’ LIST

同様の内容をnodule.hのコメント欄にも記録する.あとはCOMMONHEADERPARTマクロがすべてやってくれる.LISTにも仮想関数の実装が必要だ.これで使えるようになった.いや,まだある.CleanSansyo,Dispose,dumpの3つの関数だけは実装しなくてはならない.これらはCOMMONHEADERPARTの中で定義されている.NODULEファミリィの共通ヘッダ定義にはCOMMONHEADERSHORTとCOMMONHEADERPARTがあるが,どう違うのだろう?

COMMONHEADERPARTはCOMMONHEADERSHORTに上記3つの関数を追加したものだ.これらを必要としないクラスはCOMMONHEADERSHORTを選択することができる.COMMONHEADERSHORTを使っているクラスには,BASETABLE,NLIST,nlist,ARRAY,PSCLASSがある.クラス定義内で実装する必要がある場合にそうしているのではないか?テンプレートクラスの場合はその方が対処し易い.NODEREFLISTも手抜きしてCOMMONHEADERSHORTを使うことにした.

実行できるようになった.修正を戻した版も実行可能であることを確認しておこう.⇒問題なさそうだ.こんどはBobjectを仮想化してみよう.Bobjectには仮想関数はたくさんあるが,実装を持たない派生クラスも結構ありそうな気がする.Bobject::Drawという関数はどうか?おそらくこの関数はほとんどすべての派生クラスで実装しているはずだ.⇒中には持っていないものもある.たとえばTREEVIEW,というかこれだけだ.逆にRefreshという関数を持っているのはTREEVIEWだけだ.Refreshを廃止してDrawに統一すればよいのではないか?

Refreshを廃止してもコンパイルエラーは出ない.おそらくRefreshを使っているのはTREEVIEWだけだが,TREEVIEW::Refreshはシグネーチャが違うのでエラーにならない.この関数は引数を3つ持っているが,最後のmapmodeをMAP_DISPLAYで定数化したバージョンをTREEVIEW::Drawとしておこう.⇒これでビルドできた.

しかし,動作上の問題が出てきた.TREEVIEW::Refreshはデバイスコンテキストが空で呼び出されることを認めていないが,DrawがRedrawからcdc空で呼び出される場合がある.TREEVIEW::Refreshはあくまで実描画を目的としているので,TREEVIEW::DrawにはBobject::Drawの内容を移しておくことにする.⇒動作するようになった.少なくとも現行ではTREEVIEW::Drawは実描画には使われていない.修正を戻して動作確認する.⇒OKだ.

noduleは実装クラスだが,NODULEは仮想化できるはずだ.適当な仮想関数があるだろうか?仮想関数は山のように持っているのだが…void setpid(void)はどうか?この関数はCOMMONHEADERSHORTで定義されているのでほとんどすべてのクラスで実装されている.NODULEもこの関数の実装を持っているが,これはコンストラクタの中で一度実行されるだけなのでコンストラクタに内容を転記しておけばよいはずだ.

実装した.動作している.setpidは共通ヘッダ部定義で定義されている他はnoduleにあるだけだ.つまり,共通ヘッダ定義を持たないクラスはNODULEとnoduleだけと考えられる.NODULEは別としてnoduleが共通ヘッダ定義を使わないというのはなぜだろう?noduleは共通ヘッダ定義の関数をほとんど自前で持っているように見えるので,なぜ共通ヘッダ部定義を使わないのか理由が分からない.noduleクラスにヘッダ定義を持ち込むとどういうことになるのか調べてみよう.

共通ヘッダ定義と重複する関数を除くと,未定義変数が3つ現れる.pclassid,vsfunctable,VSLOTFUNCだ.また,nodule::_SLOTが非標準の構文とされる.⇒これらの定義がnoduleのクラス定義より後方に置かれていたためだ.DrawTreeView.cppまでコンパイルするとまたnodule.hでエラーが出始めた.

nodule::operator new’: すべてのコントロールのパス、関数を回帰するとランタイム スタック オーバーフローが発生します。

このエラーはDrawTreeView.cppで始めて発生し,それ以外のファイルでは何も起きない.MakeTribeBox.cppでも出た.RestoreTable.cpp,nodule.cppでも出る.⇒最終的にエラーが出るのはDrawTree.cpp,MakeTribeBox.cpp,RecycleSystem.cpp,ZelkovaDLL.cppだけになった.ただし,これらのファイル名がプリントされているということはコンパイル完了とみなされるので,おそらくこれらのファイルのあとにコンパイルしたファイルでエラーが出ているものと思われる.

このエラーの原因は明瞭だが,なぜこのエラーが出るファイルと出ないファイルに分かれるのかは調べておいた方がよい.C++ファイル80本のうちオブジェクトファイルができていないもの26個あった.共通点はよく分からないがBobject.cppを含め,Bobjectクラスのファイルが多いような気がする.⇒※動作から推定すると,*.hの検査と*.cppのコンパイルは別スレッドで同時進行しているように思われる.

void *nodule::operator newとnodule::operator deleteを外した共通ヘッダ部を作ってCOMMONHEADERBASICとし,COMMONHEADERSHORTはこれにこれらの関数を追加したものという構成に変えて,noduleクラスではCOMMONHEADERBASICを展開するようにした.nodulleのoperator newとdeleteをNODULEに移管できない主な理由は,オブジェクトのリサイクルをnoduleが担当しているためだ.リサイクル操作はnewとdeleteの中で実行されるので,このような仕掛けが必要となっている.

これまではnoduleは他のNODULEクラスとはまったく異なる扱いになっていたが,この整理によってoperator newとdeleteを除けば,他のクラスと全く同じ同じ構成を持つNODULEの標準的クラスとして位置付けられるようになった.修正を戻しても動作することを確認した.

SIMPLEGRAPH::attributeを廃止し,グラフのノードから参照するオブジェクトのクラスのCIDを格納するように仕様変更する.CIDが正のときは通常のnoduleクラス,負値の場合は-CIDサイズのデータが格納されるものとする.⇒実装した.

本日4回目のバックアップを取っておこう.これまでの修正をいくつかフィックスしておくことにする.まず,「SIMPLEEDGE_generationを廃止@20201020」と「NODULE:nodegeneを廃止する@20201021」は確定でよいだろう.⇒ダメだ.修正に失敗してしまった.もう一度やり直そう.大きいブロックを消すときは一旦隠蔽してから削除した方が安全だ.前者が18ヶ所,後者が47ヶ所ある.失敗すると痛いのでまた取っておこう.⇒また失敗だ.未解決の外部参照が2つも出てしまった.CARDLINK::getnodegene(void)とCARDLINK:setnodegene(int)だ.今日はもう寝たほうがいいのかもしれない…

「完全な被参照リスト」を整備する

「完全な被参照リスト」を整備するというのは積年の懸案だった.実装をためらっていたのは主に(時間的・空間的)効率上の理由からだが,CPUがここまで速くなりRAMの実装も4~8GBといういまの時代にあってはためらうべき何の理由もない.完全参照リストが完備するとこれまでのシステムメンテナンス上の苦労の過半が解消することになり,システムは思い切りシンプルなものになることだろう.とは言え,これによって参照管理が完全に自動化されるというものでもない.オブジェクトがランタイムで削除されるときシステムの恒常性を維持するためにはそれなりのことは(いままで通り)実行されなくてはならない.そこまで自動化できればプログラミングの苦労も相当軽減されて,本質的な論理構築に専念できるようになるのだが…

それでも完全な参照リストがあれば,そのオブジェクトを参照しているノードを完全に把握できるので,そのノードのクラスと参照の置かれたスロット番号が分かれば状況はかなり改善できるはずだ.この意味では完全参照リストに記載する情報にスロット番号を追記しておくことは意味があるかもしれない.そうすれば,完全参照リストは真の意味で完全なものになるとも言える.これに要するコストはそれほど大きくはないので実装すべきだろう.すでにnoduleクラスにはReferenceListという単体オブジェクトを対象とする参照リスト管理が備わっている.これはデバッグ支援を目的としているが,対象オブジェクトを全ノードに拡大すればそのまま完全参照リストシステムになり得るものだ.

すでに基盤作りは完了しているのであとはReferenceListを使う論理の拡張を行うだけになっている.修正は「完全被参照リスト管理の整備@20201104」とする.⇒とりあえず,ReferenceListをそのまま使って対象を全ノードに拡張する修正を試してみる.⇒この関数はNODULE::xpandを使うように特化されているので,別建てにした方がよい.⇒nodule::ReferenceControlとした.⇒実装した.

一発でスタックオーバーフローしてしまった.原理的に無理なことをやっているのか?単純なミスなのか?それが問題だ.LISTノードを再帰的に生成しようとしている.参照リストはNODULEのreferistスロットに接続されるだけなので参照が発生する可能性はないと思っていたのだが,LISTは参照末尾を押さえるためにbottomという参照リンクを使っている…LISTはDATALISTの派生クラスだが,bottomはDATALISTですでに定義されている.bottomを持たなくてもリスト管理できない訳ではないが,末尾を押さえていれば要素を追加するときの便宜がある.

DATALISTは抽象クラスとみなされるので,bottomをLISTで管理するようにして,参照リストはDATALISTから直接派生するようにしたらよいのではないか?⇒それしかないような気がする.どうも結構大きな修正になってきた.DATALISTから直接派生しているのはLISTしかなかったと思うが,システム図で確認しておこう.⇒確かにそのようだ.

DATALISTからbottomを除去するのは簡単にできた.あとは,DATALISTで除去した分をLISTに移すだけだ.⇒完了した.スタックオーバーフローは発生しなくなったが,参照カウントの不一致が発生している.これを見る前に参照リストをDATALISTから直接派生しなくてはならない.⇒これはnewステートメントを書き換えるだけで済むが,LISTの関数を使っているので,それを整備しなくてはならない.findlinkとremoveだ.この2つは逆にDATALISTに移入してやることにしよう.この2つの関数はLISTにも残しておく必要がある.findlist,precedingも必要.⇒LISTに残しておくべきいくつかの関数をDATALISTで直接書き換えてしまった.⇒バックアップから復元した.

ようやく動き始めたが,どこか壊してしまったようだ.TOPOLOGY:FindJikusenAncestorで枝グラフの操作中にエラーが出ている.グラフの枝リストはLISTの派生クラスであるNLISTだが,toplistが存在しているのにbottomlistが空になっている.いや,リスト自体は健全だ.datacount=0でtoplist,bottomlistともに空になっている.エラーが発生しているのはputを連結する位置のノードをthisで初期化しているためだ.⇒違うところを見ていた.テンプレートクラスの関数はクラス内で定義されていてもシグネーチャごとに実装されているので,動いているのは別ものだ.確かにtoplistが非空でbottomが空という状態になっている.datacountはゼロなのにtoplistが空になっていない.

toplistはDATALISTが設定しているので,これはDATALISTの論理の誤りと思われる.おかしい.LIST::_removeでもtoplistを更新している場所が見えない.リスト要素はtoplistに接続しているので明示的にリンクしなくてもdeleteなどのアクションで自動的に更新されるようになっている.それをやっているのはオブジェクトのデストラクタではないかと思うのだが…この枝リストが空になるタイミングを掴まえるしかないのではないか?⇒こういうときは迷わずSWOを使うしかない.

不良は#1450のEDGELILSTで起きている.⇒カーソルが動かなくなってしまった.開発機だけでなくサブマシンもハング状態になっている.サブマシンを再起動して復旧した.SWOの設定を誤っていたのが原因と思われる.SWOはCPUパワーをいきなり食うのでヒットしないで爆走するようになるとすべてが止まってしまう可能性がある.

SIMPLEGRAPH::add→NLIST::putでnew SIMPLEEDGEを実行→SIMPLEEDGEのコンストラクタで停止した.ここでtriggerを掛けてもう一度走らせてみよう.LIST::_removeを実行しているところだ.

▲NODULE::_SLOTの論理はかなりおかしい.これではまったく動作しないように思われる.(edan >= 0)のとき,

if (edan <= SLOTsNODULE) return (nodule*&)bnod[edan];
else throw ERR_WRONGSLOTINDEX;

となっているが,SLOTsNODULEは定数で値はゼロだ.この関数はNODULEのメンバーで仮想関数ではないから,決めつけの動作になっている.これを呼び出しているのはNODULE::checkpnodeという検査関数でデバッグ時しか実行されていないが…対象ノードはnodl_float中だが,すべてのスロットにアクセスできなければおかしい.checkpnodeの参照は14箇所.いや,_SLOTという関数はCOMMONHEADERSHORTで定義されているので,すべてのクラスが固有関数を持っている.従って,NODULE::_SLOTがこのような動作になっているというのは必ずしも誤りとは言えない.しかし,明らかにcheckpnodeが意図している動作にはなっていない._DEBUG_で止めてある関数いは少し仕様が古いものがあるのではないだろうか?デバッグルーチンのデバッグになってしまうので,あとで見ることにしよう.

対象となっているリスト要素のSIMPLEEDGEがnodl_floatに入ったときには,すでにpnodは空になっている.pnumもゼロだ.nodule:operator deleteではオブジェクトはすべてゴミ箱に一旦廃棄される.これを実行しているのはTRASHCAN::DumpWasteだ.ここに入った時点ですでにpnodは空だ.nodule::Disposeでnodl_floatを実行している.ここではtoplistは一旦空になっている.明らかにここで誤っている.増設スロットEXTRA_ANCHORの動作をコピーしてすっかり同じことをやっている.これは完全な誤りだ.

nodl_floatは接続を切断するための操作であり,参照操作を行うことは予定されていない.ノードを削除する目的のときは事前に参照はすべて削除されているはずだが,そうでない場合もある.たとえば活きたまま別の位置に繋ぎ替えるときなどにもnodl_floatが実施される.従って,ここでは参照リストに関しては何もしないというのが正しい.⇒動くようになったが,DATALIST::cancelでデータカウント残が出ている.

障害が起きているのは要素を23個持ったCOMPLISTで先頭ノードを1個削除しただけでtoplistが空になっている.cancelはもともとDATALISTの関数だが,DATALISTはbottomlistを持っていないのでLISTでcancelしなくてはならない.ただし,これはいまの障害の動作とは関係ない.ゼロスロットの繋ぎ変えはelseケースで実行していたが,REFERISTのコードが残っていたため実行されなかった.

かなり難しい問題が出てきた.フロート状態のオブジェクトの参照管理は不可能かもしれない.ともかく一度バックアップを取っておこう.

◎検索ボックスをタイトルバーに出す.もし,それができればつねに検索ボックスが見えるようにすることも可能になるのだが…

nodule::ReferenceControlで(!referist || !*referist)により停止する.*referistが空になっている.これはこのノードがフロート状態になっていることを意味する.referistは参照元ノードのアドレスが格納されているアドレスだが,参照リストに保存するのは単なるアドレスで問題ないのではないか?一度生成されたオブジェクトは生成された場所から移動することはあり得ないと考えられる.ただし,この場合に問題になるのは無断で削除された場合にどうなるか?という点だ.これについては少し吟味しなくてはならない.

このシステムは自前でリサイクルシステムを持っていてdeleteでパージされたオブジェクトも実体的には(ゴミ箱の中で)存続しているので,GP例外のような致命的なエラーは発生しないと考えられる.どこにも接続していないフロート状態というのはかなり不安定な状態ではあるが,完全参照リストシステム自体は接続とは無関係に,つまりフロート状態のノードを含めて維持できるはずであるし,そうしなくてはならないと思う.⇒修正を入れてみよう.

DATALIST::findlinkという関数がダブルポインタを使っている.ダブルポインタはスロットに格納されていないと取得できないが,代用として&を付けるという便法がある.これで通るはずだ.⇒#8のTITLEBOX→#6のTREEVIEWという参照を解除しようとして,TREEVIEWの参照リストに記載がないというエラーになった.おかしい.目の錯覚だろうか?.⇒TITLEBOX→TREEVIEWではなく,TREEVIEW→TITLEBOXになっている.#8 TITLEBOX は一度も参照設定していないのに,参照リンクに値が入っている.これはかなりおかしい.BOBsYUPPER(3)という枝だ.⇒いや,やっている.InitTreeView→initializeでSetBranchからSansyoが実行されている.

これは誤動作ではないかと思う.ノードAからノードBへの参照が多重に掛かる場合があり得る.それに対処していないのでは?参照削除されたときは単純にリストから削除しているが,これは明らかに誤りだ.A(i)→BとA(j)→Bは異なるものとして扱うか,ないし参照カウントを管理しなくてはならない.もっとも確実なのは(参照元,枝番号)の対で1エントリとする方式ではないだろうか?このためには(参照元,枝番号)というタイプのクラスないし,構造体の定義が必要だ.クラスにするのも大げさなので構造体を作ってみよう.構造体などしばらく使っていないので構文を忘れてしまった.こんな↓感じでいいかな?

typedef struct _NODEREF {
     nodule *node;
     int edan;
} NODEREF;

しかし,DATALISTと言いながら,データの一致で検索する関数を持っていないとは…nodule *DATALIST::finddata(void *dp)という関数を作った.⇒これで問題は解決したように思われが,またtoplistがあってbottomlistがないという問題が再燃してしまった.さっきは,修正コードに手抜きがあったためだが,こんどは何だろう?障害はTRIBEBOX::CheckGeneSplitで起きている.TRIBEBOX::SpllitListというLISTのインスタンスだ.

見落としていたが,ARRAYクラスの派生クラスとしてREFLINKというクラスがある.これはスロットを1個だけ持つnoduleだ.リスト要素として用いることが想定されている.DATALISTではデータサイズを指定しないときはデフォルトでこのタイプの要素を持つリストを生成する.

障害はDATALIST::putを実行したところで起きている.putはもともとDATALISTの関数だが,LISTの関数でオーバーライドしないとまずい.実際,DATALISTのputではBottomlistが欠けている.不足しているLISTの関数をすべて補充して動作するようになった.それにしても,A→Bへの参照が多重に掛かるという問題などはプロトタイプのReferenceListのときから存在していたはずだが,一度も露見したことがないというのは怖い話だ.ReferenceListはかなり強力なツールなので結構使っているにも関わらず一度も発現したことはない.A→Bへの参照が複数存在するノードというのはかなり特殊で,たまたまそれらに関係するトラブルが発生しなかったということだが,「動いているから安心」など決して言えないことが分かった.⇒ダメだ!まだ何かある.

▲構成図 TEST.zelをUNDOCHAINで開いて,graph3でソートしただけでエラーになった.被参照カウントの残留だ.障害はnoduleのデストラクタで検出されている.TOPOLOGY::topologicalSortの冒頭でbaselistをキャンセルしたところだ.障害が起きているのは#43490のPAIRBOXだ.このノードの完全参照リストを見ると確かに要素が1個残っている.ダンプしてみよう.

DumpRefList PAIRBOX::Dispose #43490 ▲【p PAIRBOX 43490】
   1 edan=2 node=PAIRLIST:4647 Pindex=0 datacount=0 gene=-1
   2 edan=2 node=#28360 ノード対:MDB(2)→(0) ch=2
   3 edan=7 node=COUPLING(0) Bobject::pid=B cid=n DEL=0 #765 nam0=NAMEBOX nam1=765
   4 edan=18 node=COUPLING(1) Bobject::pid=B cid=n DEL=0 #43373 nam0=NAMEBOX nam1=43373
   5 edan=3 node=PAIRLIST:4647 Pindex=0 datacount=0 gene=-1

枝2と3にPAIRLIST #4647 からの参照がある.残っているのは枝2の方だ.PAIRLISTの枝2はbottom2,枝3はcurnodなのでbottom2が残っているように見える.PAIRLISTは独自のcancel関数を持っている.

モノシステムとヒモノシステム

環境もようやく整って,いよいよ本格的なデバッグの領域に踏み込もうとするところ.ZTシステム構成図は開発支援のためにも十分役に立つということが分かってきたので,遊んでいるVAIO機で開いて常時参照できるようにした.システム構成図は完成にはほど遠いが,「全体図は隠蔽する(見ない)」という方針になったので遠慮なく細部データを書き込んでよい.クラスの(仮想的な)代表オブジェクトの呼び方を決めておこう.一案としてプリミティブという名前を考えてみたが,しっくりこない.オブジェクティブというのはどうだろう?まだましなような気もするが…マスターオブジェクトとか…マスタリィとか?

抽象グラフ検証系では「SIMPLENODEsLINKを廃止@20201019」は一旦放棄してもう一度組み直すしかないと思う.つまり,従来方式を完全復活させそれと並立する形で非オブジェクトも対象とすることができるようなシステムだ.グラフクラスにmonoというフラグを置いて切り替えできるようにする.この値はSIMPLEGRAPHのコンストラクタで設定できるようにすればよい.デフォルトではオンだ.monoがオンとなる対象システムをモノシステム,そうでないものをヒモノシステムと呼ぶ.ヒモノとはオブジェクト(NODULEクラス)ではないようなモノだ.この方式変更に関わるのはほとんどSIMPLENODEに限られているはずだ.今日はまず,ここから入ることにする.

「SIMPLENODEsLINKを廃止@20201019」を撤廃して,「抽象グラフ検証系の複線化@20201103」を導入する.一つ問題がある.ZTシステムではすべてのオブジェクトのリサイクルを実施している.リサイクル時にはコンストラクタが呼び出されるという理解でよいのか?また,仮にそうであったとしても,この場合コンストラクタを引数付きで呼び出すということはできないのではないか?リサイクルシステムの論理は高度に難解なところがあるが,new operatorで生成するようになっているのでコンストラクタの呼び出しは掛かるようになっていると思う.

ただし,CARDLINK, MARGLINK, PARTIALNAMEだけはsetNring でWASHINGというリテラルでnewを実行している.リサイクルは delete されてゴミ箱に入っているオブジェクトだけが再生の対象となるので,システムに常設されているオブジェクトがリサイクルされる可能性はゼロだ.現行論理ではすべてのグラフオブジェクトは一度生成された後は廃棄されることはないので,この意味では安全であるとは言える.いや,仮にリサイクルされたとしてもオブジェクトをnewで生成する位置で引数付きコンストラクタを呼び出しているはずだから,問題ないと思われる.つまり,リサイクルされる場合も新たに生成される場合も完全に同一手順で生成されると考えてよい.ただし,WASHINGという機構についてはあとで検証(して文書化)した方がよい.

SIMPLEGRAPHにはunsigned int atribute グラフ属性というのを持っている.新たにmonoを増設する必要はないのでは?⇒そうすることにしよう.⇒改修は大体終わって動作しているが,「抽象グラフ検証系の複線化@20201103」を止めて元のバージョンが動作することを確認しようとしてMaeHasseDiagram→UpStreamHasseで問題が発生した.ここで使われているグラフのノードは人名カードか連結成分リストと思っていたが,ノードリストが入ってきた.連結リストとノードリストが混在しているというイメージだ.

COMPLISTとNODELISTの違いをシステム図から読み取ろうとしたが,まだ細部が書き込まれていないこともあって比較できない.前者はnlistで後者はNLIST.おそらくnlistは任意のnoduleを要素とし,NLISTは型の決まったノードのリストなのではないかと思う.NLISTにはクラス型を引数として渡しているが,図面からは読み取れない(まだまだ未完成だ).NODELISTはSIMPLENODEをリスト要素としているはずだ.COMPLISTの要素はSIMPLENODEかないしCOMPLISTだったのではないかと思う.どうもどこかで誤動作しているのではないかという気がする.⇒単純ミスだった.

▲UndoChainでソートしてPAIRBOX::repairCommonEndPointで(CheckSamePoint())により停止した.RepairCommonEndPointでも同じ理由で停止する.サンプルはモジュール構成図 TEST.ZEL.

▲ファイル→プロパティ→場所でファイル名が表示されていない.ファイル名にスペースが入っているためだろうか?

▲かなりまずい.非参照カウントの残留が発生している.UNDOCHAINで立ち上げたあと,undochainで起きた.COUPLING:TopologicalSortの入口でResetExperimentを実行して初期化しているところで起きている.まだかなりの不備があるようだ.これは直しておかないと命取りになる.まず,SIMPLENODEのデストラクタを見てみよう.いや,そうではない.参照が残っているのはCOMPLISTだ.構成図 TEST.ZELを開いて閉じるだけで起きる.今度はPAIRBOXに移った.PAIRBOX #653だ.どうもどこかでxpand(NODULEの静的変数)をリセットしているようだ.

new (NULL, XPAND_ANCHOR) LIST(sizeof(long));

を実行してもxpandに値が設定されない.extraを使っているのではないか?いや,extraにも入ってこない.暫定的に強制的にxpandを設定するようにした.これで一応nodule::ReferenceListは動作するようになったが,非参照カウントの残留は残っている.障害ノードはPAIRBOX #653で変わらない.残留が4 もある.すべてSIMPLENODEからの参照と思われる.SIMPLENODE→PAIRBOXの参照はGENEBOX::CheckInverseCycle(危険対枝グラフの循環検定)で実施されている.参照カウントの残留はTOPOLOGYのデストラクタでCleanSlotを実施する中で起きている.

CleanSlotはCOMMONHEADERSHORTマクロで定義され,すべてのクラスに備わった常備関数になっている.CleanSlotは個別スロットごとにclearslotを呼び出してスロットを解体する.この解体処理はスロット並びに従って実施されるので,解体が最下層から逐次実施されることを予定することはできないので,自ノードを参照する可能性があるノードを探索してあらかじめ参照解除を実施する必要がある.つまり,あるノードは自分がどこから参照されているかを知っている必要がある.

CleanSlotはマクロの中で定義されているが,NODULEないしnoduleのクラスメンバとして一つだけ作るということはできないのだろうか?⇒そうするためには仮想化する必要があり,仮想化した場合にはクラスごとに実装が必要になる.この構成は避けられない.Disposeの中でCleanSansyoを実行するというマナーだ.PAIRBOXのDisposeではgraph1に対してrelease(this)を実行している.

解決した.修正に抜けがあった.一つだけ不審な点がある.OUTPUTLOG_SANSYOがオンのときアプリ終了しても画面が残ってしまう.⇒CallSetCouplingPtrで無限ループしている.仮修正で入れた不正規なリンクがそのまま残っているためだ.xpandに関わる仕様を整理する必要がある.xpandは現在NODULEの静的変数としてシステムに1個だけ存在しているが,これを個別にもたせて包括的に参照リストを管理してみたい.この仕掛けは基本的にデバッグ支援のためだが,もし時間効率がそれほど悪くなければリリース版で採用される可能性もある.

いずれにしても,改変はまず現在の仕様できちんと動くようにしてから取り掛かるべきだろう.ここまでの修正もかなり大きいので一度バックアップを取るのが賢明だろう.⇒枝番号にXPAND_ANCHORを指定したとき,無動作で抜けている.nodule::insert_nodeにはそれらしき処理がはいっているが,motoが空の場合がある@20181014でスルーするようになっている.この修正は明らかに誤りと思われる.⇒無条件に実行するように修正して動作するようになった.この版を改めて保存しておこう.直前に保存した版と差し替えておく.

その前にEXTRA_ANCHRという特殊スロットの扱い方を見ておこう.これは普通の枝番号と同様にスロット配列番号として使えるリテラルだが,多分実際にはNODULEのextraスロッを使っているものと思われる.用途は2つあり,PAIRBOXでは「端点共有ノード対接続チェーン」のアンカーとして,MARGBOXでは「SymmetricActionでブロック移動実行時に使われるリストを一時的に接続する」ために使われる.これらはすべて一時的にしか使われないものなのでそれ専用のスロットをクラスに設けることはムダと考えられたためと思われる.

今回予定している「完全参照リスト」は恒常的に使われるものだが,すべてのノードに割り当てられるものなのでNODULEクラスに設置する.アクセスはEXTRA_ANCHORのやり方に準ずるものとなる.このスロット番号には別の名前を割り当てることにしよう.参照リスト専用なのでREFERLISTでよいのではないか?REFERISTとしてみよう.スロット名はreferistとしておく.@20201103@でマーキングしておこう.この仕組みは既存システムをまったく改変しないで上から乗り込むだけとし,従前システムとの切り替えスイッチは設けない.

昨日作った要対処項目リストの整理に失敗

昨日作った要対処項目リストの整理に失敗してしまった.オリジナルでは92件あったのに,82件に減ってしまっている.行の移動で何度もUNDO/REDOを繰り返しているので何か誤操作ないし誤動作があったのだろう.中間で保存している可能性もあるので読み出してみよう.⇒下書きは残っていない.!偉い!カテゴリ分けした時点で公開している.ここからやり直せばよい.HTMLのリスト形式になっているが,むしろプレーンなテキストエディタで編集したあとリストに仕立て直した方が確実なのではないか?⇒テキストエディタから戻してリストに変換しても改行が無視されてしまう.⇒やむを得ない.どっちみち行番号も消さなくてはならないのだから…

行番号が付くのを避ける方法がある.HTMLで一度リストを解除してやればよい.テキストエディタで一改行入れておくと一発でリストに変換できる可能性がある.⇒あっという間に終わった.1項目を分割しているところがあるので,93項目になった.全93項目のうち,バグ37件,不良20件,改善24件,検証5件,宿題1件,参考6件に分類された.バグは障害の発生事例,不良は仕様的な不具合,改善は改良のための提案,検証は確認を要する事項,宿題はやや長期的な課題,参考はプログラムに直接影響しない情報.この手順で一つだけ問題がある.リンクが切れてしまうという点だ.これは適宜復元するしかないだろう.

すでにフィックスしている項目は対処済み項目リストに移動する.ともかく最初の37件のバグから見てゆくことにしよう.バグ項目には「障害を再現させるための手順」が記録されていることが必須だが,ほとんどの項目はその要件を欠いているので,まずそこから始めるしかない.最初に再現可能か否かを確認するというところから始めよう.再現出来ない項目はとりあえず,後回しとするしかないが,再現可能な項目でも緊急性や難易度の観点から判断して後回しとすることもあるだろう.

いや,その前に机上に反例サンプルが10個あるので確認しておこう.BUG20-10-31というフォルダに収容した.⇒一つだけ開けてみたが再現しない.多分これは広域スプリット検定の反例ではないかと思う.「ファイルオープンテスト」を実行してみる.特に問題なく開けた.多分すでに解消しているのではないかと思う.

▲実親を基準ノードでソートしているのに直下に来ないというのはかなりおかしい 

再現できるだろうか?これは軸線図だ.UNDOCHAINでソートしているのだが…この記録は10月23日のものだが,現在のシステム図とは大分違うようだ.ごく初期のモジュール構成図と呼んでいた時期のものと思われる.図柄は多少違うが,確かに出てきた.

image

UNDOCHAINには父が3人いてそのうちUndoChainは実親,他の2人は養親となっている.この2人が養親子関係であるのにブルー表示されていないというのは,これらの父が祖父undosysの実子であるからだ.つまり,この2人の義父は同時に叔父でもあるため,傍系血族のピンク色になっている.いずれにしても血統軸線図では直系血族が最優先されるはずなのでUndoChainが軸線上にないというのは誤りと考えられる.軸線図関係はJikusen.cppというファイルがあるので少し読んでみよう.

「複数出力枝を持つ」というダンプが出ているが,これは軸線図に関係するもののようだ.このダンプはTOPOLOGY::FindJikusenAncestorのステージ【6.1】父系/母系優先を枝グラフに反映するで出ている.

VAIOに最新版をインストールしようとしてエラーになった.

image

このマシンにはゼルコバの木ベータ2はインストールされているが,ZTはまだインストールされていない.Program Files (x86)のbabalaboにはZelkova Beta2フォルダがあるだけだ.つまり,まだバージンと言ってよい.CPUもOSも64ビットだ.考えられるのは.NETのバージョンくらいだが….NETは3.5が入っている.アプリの必須コンポーネントは.NET 3.5 SP1だ.HRESULT=-2147024770=0x8007007EはDLLNotFoundExceptionだ.いや,OCXの登録に失敗しましたとある.⇒インストールに成功した.必要条件はVisual C++ 2017 Redistributable (x86)がインストールされていることだ.VAIOにはVC++ 2010 Redistributableしかインストールされていなかったため,マイクロソフトサポートからダウンロードしてインストールした.

VBの発行→必須コンポーネントにはVisual C++ “14” Runtime Libraries (x86)が必須となっているが,msiインストーラはこの手続きを踏んで生成されている訳ではないので,不足しているコンポーネントを指摘できないのだろう.「発行」を実行するとデスクトップ上にApplication Filesというフォルダが作られる.これは発行フォルダの場所にデスクトップを指定しているためだ.このApplication Filesというのはネット上のサイトに置くべきもので中に入っているものの大部分は*.deployという拡張子が付いている.どういう使い方をするものなのかはよくわからない.この方法でインストールした場合にはバージョンアップしたときの自動更新などもサポートされるのではないかと思う.いずれにしても,少なくとも64bit OSで64bit CPUならZT 2021をインストールできることが証明された.この後の課題としてVisual Studio 2019への移行というのがあるのだが,それはまた後日.

「複数出力枝を持つ」というダンプが出ているので,このサンプルには何か特殊な問題があるらしいということが察せられる.「枝」と言っているのは「グラフ」の「辺」のことで,このロジックでは抽象グラフ検証系のSIMPLEGRAPHを使っている.グラフはすべてTOPOLOGYが管理しているのだが,何を使っているのだろう?graph2だ.graph1, 2, 3があるが用途が分かるようにJikusenGaphとリネームしておこう.おそらくこの枝グラフは基準ノードから先祖ノードまでの直上経路を探すためのものと思われる.仮に下から上に矢線が向いているとすれば,出力枝とは上向きの枝ということになる.

すでにSIMPLELINKのノードを廃止してしまったが,早まったかなという感じもある.ただのベタ参照とnodule*参照では使い勝手がまったく違う.確かに早まっていたと思う.NODULEではスロットに数値を格納することはまったく不可能という訳ではなかったのだ!昔はそういうこともやっていた.ただし,4バイトのスロットのうち2バイトまでしか使えないので,-32768~32767ないし,0~65535までだ.しかし,一般の用途ならこれで十分なのではないだろうか?Windowsの座標値の範囲も概ねこのくらいだったような気がする.⇒いや,ちょっと勘違いしている.SIMPLENODEのlinkに入るのは数値ではなくアドレスだ.これには4バイトが必要だ.

スロットとベタ参照を両方用意して使い分けることも考えられる.そういうことを意識せずにプログラミングできるところがNODULEの最大のメリットだった…つまらない欲をかいてしまったのだろうか?いくらなんでもベタのアドレスとNODULEのポインタをごしゃまぜにはできない…こうなることは覚悟の上だったはずだが…失ったものはあまりに大きい.やはり,この修正「SIMPLENODEsLINKを廃止@20201019」はあまりよくなかったと結論付けるしかない.修正箇所は45箇所ある.

もう少し考えてみよう…これまでの仕組みの利点を失わないためには,装置を複線化するしかない.つまり,たとえばlinkとvalueを使い分けるという方式だ.グラフはlinkベースであるか,valueベースであるかのいずれかしかないと考えられるから,グラフにフラグを持たせてそれをSIMPLENODEが参照して切り替えればよい.これは結局対象がNODULEベースであるか?NON-NODULEベースであるかの違いということになる.このフラグを bool monoとする.グラフはデフォルトではmonoだが,生成時ないし動的に値を設定できるものとする.

▲メニューバーの検索ボックスは最初カーソルがボックス中央に出ているが,文字入力すると左端に移動してしまう.最初から左端に出している方が素直だ

システム構成図でsplitlistという名前は2箇所出てくる.TRIBEBOX::splitlistとTRIBELIST::splitlistだ.前者はすでにSplitListとリネームされているがシステムズには反映されていない.プログラムの書き換えとシステム図のメンテナンスを同時並行されるのは至難の業だ.完全に同期させるためにはプログラムのソースコードから自動生成できなくてはならない.

後者の用途は「系列間シンメトリ婚スプリット検定用リストへの参照」となっている.広域スプリット検定ではリストは使っていないはずなのだが…⇒確かに,このオブジェクトは全く参照されていない.⇒いや,使われている.関数の中でクラスメンバー変数をわざわざローカル変数に置き換えているが,意味がないと思う.相手を呼ぶときは固有名詞で呼ぶべきだ.⇒対処した.

TRIBELIST::CheckSymmetricSplitとTRIBEBOX::CheckTribeSplitはほとんど等価なのではないか?どちらもLISTを使っているが,なぜリストが固有データ部を使っているのかという理由も分かる.動機はいま上で問題になっている非monoデータの参照に関係がある.2つのスプリットリストは連結問題をグラフを使わずに解いているが,確かに実際的にはこちらの方が圧倒的に効率的だ.グラフを使った場合には,最初に枝を生成し(頂点集合がすでに生成済みないし,枝を生成するときに同時に生成),次に連結成分分解を実行しなくてはならないが,スプリットリストの方法では最初に頂点集合を作ったあとは頂点集合自体が集結して最終的には連結成分そのものになるという手順になっている.

このようなことが可能になるのは,頂点と連結成分が同種のものと考えられるためだ.つまり,頂点は区間であり,連結成分も区間になる.しかし,通常は連結成分は頂点の集合であり,頂点そのものではない.言い換えると元と元の集合が同じ形式で表現可能ならスプリットリストのような手法が適用できる.少なくともTRIBELISTのスプリットリストとTRIBEBOXのスプリットリストは共通関数によって生成されるようにすべきだろう.多分それほど難しくはないのではないかと思う.グラフを使わずにグラフ問題が解けるというのはある意味おもしろい.

◎全体図という部分図を既定で生成する.

システム構成図のデータ入力を通じたバグの掘り出し

ゼルコバの木システム構成図のデータ入力を通じたバグの掘り出しとそのシューティングは順調に進んでいると言ってよいだろう.データ入力はここで一旦中断することにしているが,本線のDrawingObjectクラスの導入に移る前に一度これまでのバックログを見直してできれば一掃してしまうのがよいのではないか?まず,昨日の修正で気付いたいくつかのポイントをメモ書きしておこう.

  1. スプリット検定をグラフの連結性の問題として解く
  2. 全体図は隠蔽される 親族図を活用する
  3. ※クラス名でクラスインスタンスを表示する
  4. あいまい検索では大文字と小文字を区別しない
  5. cardQのリングの使い方 リングをテンプレートクラスにする
  6. リリース版作成時の手順リストを作る
  7. クラスの固有データ部を追加する

スプリット検定には2種がある.系列内スプリットを扱うCheckTribeSplitと,広域的なスプリットを無差別に検査するCheckHSplitsだ.前者と後者では方式が大きく異なる.後者は縦長の検査枠をずらしながら手続き的に検査する方式だが,前者はオブジェクトの矩形領域を水平区間に変換し,交叉している区間を一つの区間にまとめて最終的にそれらが一つの区間に集約されない場合をスプリットと認定している.これは手法的には完全にグラフの連結性判定と同じであり,節点をオブジェクトの水平区間,枝を水平区間の交叉関係とするグラフを連結成分に分解してやればそれがそのまま回答になると考えられる.系列内スプリット検定ではSPLITLISTというリストクラスを使ってこの検査を実施しているが,このリストはSIMPLEGRAPHのCOMPLISTに(連結成分リスト)相当する.検定では交叉する2つの区間を一つに合併しながら検定を進めるという手順になっているが,このようなカスタマイズは抽象グラフ検証系でもできることだ.

後者の広域スプリット検定に関して言えば,現行方式では結婚枠間ギャップよりかなり広いスプリットを見落とす可能性がある.現行の値を使えば検査枠の幅は16,結婚枠間ギャップは15なので,最大で幅30のスプリットが見落とされる可能性がある.これは現行方式では検査枠を検査枠幅を単位にずらしているためで,このスライド量をより小さくすることでこのような事態を回避することは可能になる.たとえば,スライド量を1とすれば15より大きなスプリットを見逃すことは100%起こり得ない.しかしこれではコストが掛かり過ぎだ.許容されるスプリット幅をS,検査幅をΔ,スライド量をδとするとき,

S = Δ + δ -2

となるから,Δ=16,δをその半分の8とすれば,コストは現状の2倍,許容スプリット幅(検出不能スプリットの最大値)は22となる.δ ≦Δ ≦ 検出すべきスプリットの最小値=許容スプリット幅+1,結婚枠間ギャップをGとして Δ = G +1のとき,S = G + δ ー1に S = G を与えると,δ =1を得る.つまり,結婚枠間ギャップ以上のスプリットを見逃さないためにはスライド量を1とするしかない.許容スプリット幅30というのは微妙だが,δ値を1≦δ≦Δの間で調整することで許容スプリット幅を狭めることは可能だ.

ZTシステム構成図7.ZELの部分図「クラス継承図」をNODULEでソートして全体図に戻ろうとしたところで出口検査:InvestigateHSplitsが出る.しかし,全体図ではスプリットは見当たらない.⇒バージョンが古いのではないか?⇒バージョンは同じだが,開発環境では再現しない.最新版を作ってインストールし直してみよう.⇒※すでに解決.

おかしい.ゼルコバの木はインストールされているはずだが,コントロールパネル→プログラムと機能で表示されない.机上にはZTアイコンがあって起動できる…Program Files(x86)にはbabalaboフォルダがあり,コンポーネントはすべてそろっている…ダメだ.OCXが差し替わっていない.プログラムと機能には表示されていない.インストールしたプログラムがプログラムと機能に表示されないというのは今回始めてのような気がする.⇒アホなことをしていたような.サブマシンの画面を操作していた.マルチスクリーンをビデオ切替スイッチで切り替えて使っているのでこんなことも起きる…アプリが12本しかインストールされていないのでおかしいな~とは思ったが…開発用メイン機には120個近くのアプリがインストールされている!

ZTシステム構成図の現在の登録点数は189でそれほど大きなものではないが,養親子関係が多重に掛かって相当混雑した図面になっているためデータ入力の意欲もそがれてしまうが,「全体図は隠蔽されるもの」だと決めつけてしまえば,かなり気楽にデータの追加もできるようになる.ZTシステムがデータベースを持つようになって,10万件,100万件のデータが蓄積されるようになったら,もちろん全体図を描画するなどということは夢想することもできない.この意味では「親族図ないし部分図を活用する」というのが今後のZTの進むべき方向だろう.現行では構成図にはNODULEクラスしか登録されていないが,この図面を傍らに置いて参照しながらプログラミングするとすれば,固有データ部の構成も必要になってくる.そうなると全体図はさらにますます膨張することになるが,全体図を見ないことにすれば項目7で取り上げた「クラスの固有データを追加する」ことも現実的な課題となるだろう.

クラス名は大文字で書くようにしているが,必ずしも徹底していない.例外はあまり多くないので調整してもよいが,構成図でクラス名とクラスの代表インスタンス名が同じというのはあまりよくないと思う.しかし,これに小文字名を与えるというのもよい考えではない.インスタンス名は実在するものでなくてはならない.NODULEシステムではある意味で無名のオブジェクトが無数にあると言ってもよいので,それらをクラス名で代表しておくというのは妥当だが,クラスと実装を区分するために何か修飾を加える必要がある.名前の末尾に♂,♀を付けるというのでもよいが,頭に※印を付けるくらいが妥当だろう.

ゼルコバの木の検索はカード編集→カード検索で「姓名の完全一致」を指定しない限り文字列の一部が一致したものを列挙するような「あいまい検索」になっているが,英字に関しては厳密に大文字・小文字を区別しているというのは片手落ちだ.ゼルコバの木のユーザはいまのところ日本語ユーザに限定されているので,急を要する話ではないが,将来的には対処する必要があると思う.

cardQなどというものを使っていたこと自体忘れてしまっているが,読み直すとリングという機能を一部使っているようだ.リストや参照チェーンなどの操作はゼルコバの木では生命線であり,NODULEという基底クラスの基本機能と考えられるので,これらを整理して完全に汎用化することが強く求められている.リングやリストをテンプレートクラスにするというのも悪いアイディアではないような気がする.ただちに着手する課題ではないが,検討を要するとしておこう.

リリース版をインストールするたびにアンインストールするというのは好ましくないし,ユーザは上書きインストールするものだから「インストールの実機テスト」としての意味もない.パッケージの中で上書きインストールされないのはOCXだけのようで,バージョンを更新してやればインストールできることは確認されている.漏れのないようにリリース版作成時の手順リストをまとめておく必要があるが,まだやっていないのはプリンタのインク切れということになっているからだ…このところ家から一歩も出ない日が続いているのでなかなかそこまで手が回らない.ネットで購入という手もあるけど…あ,モノクロならtamo2さんのプリンタがある.なんでそれに気付かなかったのだろう?

ログを読み直してバックログを拾い出すという作業も確立した手順があったのだが,すっかり忘れてしまった.今回は「再開発スタート版」から始めているのでとりあえずはそこまで戻ればよいと思う.再開発スタート版というのは2020/10/23に始まっている.今回のセッションは2020/09/15の「作業に復帰した模様」からスタートしているのでそこから読み直してみることにする.

要対処項目リスト(93件):2020/09/15~2020/11/01

  1. バグ:実親を基準ノードでソートしているのに直下に来ないというのはかなりおかしい
  2. バグ:血統軸線図で実子が軸線上に来ないというバグがある
  3. バグ:カード合併を実行→UNDO…→REDO…でSTOPパネルが出て止まらなくなった 反例サンプルは保存されているはずだが,ファイル名は不明
  4. バグ:#33 baselistの子ども氏名欄の「NLIST」に「NLIST <GENEBOX, ‘g’>」を上書き→登録でエラー「結婚リンクが消えていない」が発生した 反例サンプルはゼルコバの木モジュール構成図(ERR1).ZEL
  5. バグ:UNDOメニューがすでに不能状態になっているときにCTRL+Zを押してエラーになった
  6. バグ:kakeizuの子どもとしてnoduleを登録→リネームで「結婚リンクが消えていない」エラーになった 
  7. バグ:人名カードでテキストボックスをダブルクリックしてテキストを全選択状態にしようとして戻ってしまう ⇒意味不明 入力ボックスをダブルクリックしたときは通常そのカードにジャンプするはずだ 入力中の場合は新規カードを作ってジャンプになっていると思うのだが…
  8. バグ:tajugraph2で配偶者SIMPLEGRAPHを子ども欄に移す付け替えをやっていてSTOPで停止した 反例サンプルは(ERR2)として保存した
  9. バグ:SIMPLEGRAHPHの父母ページで登録ページ数4のとき,5ページ目に移動しようとして「ページ番号不正」エラーになった 反例サンプルは(ERR3) 父母ページ数の上限は4になっていた
  10. バグ:検索ボックスでテキストを全選択してDELキーで「削除する対象カードを選択してください」が出る.OKでテキストはそのまま残っている.BSで一文字づつ消すことはできる
  11. バグ:子ども12人を登録して子ども数が11のままになっている
  12. バグ:結婚枠データの保存でMAXMARRIAGEでループしている
  13. バグ:クラス部分図を全選択→反転してTREEVIEW::selectAllで (topology->ActiveList->count) という理由で停止した
  14. バグ:クラス部分図で全選択→反転→部分図登録で作った部分図に女性カードのCOUPLINGが入っていた
  15. バグ:部分図をリネームして登録したのに部分図が3つになってしまった.そのうちの一つを削除して部分図の選択メニューが壊れてしまった
  16. バグ:構成図6で保存した このファイルを開き直そうとして,nodule::Sansyoで停止した previous->Refcount()–;を実行していて例外が発生している
  17. バグ:要素包含図で全選択したのに一覧画面ではごく一部のカードしか選択されていない.今度は動くようになった
  18. バグ:GENEBOXの父にbaselistを登録しようとしてMakePairListClean中PAIRBOX::getLocationで停止した サンプルは構成図7
  19. バグ:構成図7を開き,上図の部分図を再現しようとしてSIMPLEGRAPH:TightenHasseDiagramで「孤立ノードが存在する」エラーが発生した
  20. バグ:UNDOで戻ったときにも「孤立ノードが存在する」が発生した
  21. バグ:出口検査で多重カード1というのは明らかに間違っている.少なくともMDBとPDBは多重カードだ.tribelistも多重だし,NLISTも多重だ.当然避けられない多重1とは思われない.重婚同類循環が3もあれば多重が多発するのは避けられない
  22. バグ:treeviewの出現は2つあるが,2番目の出現は配偶者であるはずなのに頭に垂線が入っているのはおかしい.これではTREEVIEWの子どものように見える
  23. バグ:NAMEBOX::IsPossibleBTWLeftHandでASSERT_NEVER (rightwife->IsLeftHandBox())により停止した.treeviewの父母ページで母氏名をCOUPLINGとして登録しようとしたところ.UNDOで戻して(ERR4)で保存
  24. バグ:TRIBEBOX::SetMinorTribeで(primary->getrelation() == REL_SPOUSE)により停止した.starttribeから子どものTRIBEBOXを削除して配偶者に付け替えようとしたところ.UNDOで戻して(ERR5)で保存した.topUndo,UndoCurptrなどでも起こる
  25. バグ:BASETABLE<CARDLINK, MAXPDB>の本人タブの氏名をダブルクリックして「検定続行不能エラー」が発生した 反例は(ERR7)
  26. バグ:REEVIEW::GetViewSizeで(!_size->cx || !_size->cy) 描画領域空エラーで停止
  27. バグ:ZTシステム構成図(ERR7).ZELを開こうとしてTRIBELIST::GoDownStreamで(marg && !marg->Invalid() && (!marg->margbox || !marg->margbox->ISSETTLED()))により停止,その後続行して以下を出してエラー終了
  28. バグ:部分図から親族図に切り替えようとしてPAIRBOX::CalcPairBoxでASSERT_NEVER (nocommon) エラーになった.⇒再現できる.この状態で保存しておこう.(ERR8)とする
  29. バグ:NLISTの母をEDGELISTに付け替えて,CARDLINK::ScootParentPageで停止した. (!OnImportEnd && !APPENDFILE && !PrimaryMergeCard && !OnDeleteCard) 同一親ページの重複を検出
  30. バグ:所属グループの親ページからtreeview+TREEVIEWを除去して,PAIRBOX::repairCommonEndPointでDEBUG_NEVER (CheckSamePoint())が起きる
  31. バグ:ファイルを保存しようとしてMARGLINK::getOnumで(!margbase.otto)により停止した.このあと,例外が発生した
  32. バグ:SUW(Show Under Wear)が出てしまった.(ERR9)として保存したが再現できるだろうか?undoで戻ってもSUWになる
  33. バグ:構成図5として保存したファイルから部分図に移動するだけで障害が発生する ⇒SUWを止めて画面は表示できた.このファイルを(ERR10)として保存 これはmarghgapをゼロから15に変更したことが直接の原因となっている ⇒障害の原因はTRIBELIST:CheckInnerSplitで区間計算にmarghgapを加算していなかったためだ ⇒人名枠ではなく結婚枠を対象に計算するように変更した
  34. バグ:ZTシステム構成図(ERR11).ZELの部分図でズームの動作がおかしい.+をクリックして拡大する代わりに上に動いたり…画面に合わせてズームなども不調 「系統並び替えで遅延処理されるためここではパス」という理由で画面が更新されていない 部分図のタイトル情報の更新で系統並び替えフラグが立っている これは明らかに誤り ⇒PARTIAL_UPDATEのときはフラグを立てないようにした ⇒解決 
  35. バグ:障害は系列内スプリット MAXPARENTS=4.ZELで再現できる
  36. バグ:ZTシステム構成図3.ZELの出口検査でInvestigateHSplitsが検出される ⇒スプリット検定のパラメータδの仕様変更の副作用
  37. バグ:MAXPARENTS=32で保存されたサンプル→ZTシステム構成図 MAX=8.ZEL
  38. 不良:子ども並び替えでドラッグ移動が効かない⇒説明文を改訂する
  39. 不良:人名カード画面の子ども氏名欄でホイールでスクロールしたときのスクロール量が大き過ぎる ページスクロールの動作になっているようだが,次行に書き込めない 最小限一行分のダブリが必要だ
  40. 不良:検索ボックスに名前を入力して虫眼鏡をクリックしてもジャンプしない⇒選択/無選択に関わりなくつねにジャンプする
  41. 不良:カード一覧表で一覧表セル幅→均等分割するのとき全列表示に戻ってしまう
  42. 不良:ウィンドウのドッキングでウィンドウ境界に隙間ができてしまう
  43. 不良:一般に婚姻による理由以外では下流の子どもとの連結のために抽出枠を作る必要はないように思われる
  44. 不良:一覧表でカードを選択してDELで削除パネルが出ない.メニューからは実行できる
  45. 不良:部分図から親族図に戻ったとき,選択されたカードが画面内に入っていない.⇒全体図→部分図→親族図のように遷移したものと思われる.全体図では選択カードは見えている
  46. 不良:BASETABLE<CARDLINK, MAXPDB>はクラス(女性)だが,父がCARDTABLEになっている.CARDTABLEは女性のはずだ 女性カードが父となっているのは子どもカード生成ないしリンク時の動作が誤っていた可能性が高い.クラスノードを親とするほとんどのノードにこの誤りが起きている
  47. 不良:部分図でtoplistの父がDATALISTだったのを母に移動して登録したら,新しい女性カードのDATALISTが生成された.これはまずいのではないか?⇒多分DATALISTはtoplistの子どもだったのだろう.このようなケースでは自動的に新規カードを生成する
  48. 不良:toplistの母氏名にDATALISTを記入するとグリーンになるのは,実在するDATALISTカードを押さえているためと考えられるのに,実動作では新規カードが生成されるというのはどこかに誤りがあるためと考えられる.おそらく,このカードDATALISTが部分図画面上にないことが誤動作の原因になっているのではなかと考えられる
  49. 不良:下図ではnoduleが選択されているが,多重出現している.これは回避可能な多重であり,本来解決されるべきものだ
  50. 不良:tajugraph2の子ども氏名欄をクリックしただけで,以下のようなパネルが出る.まったく意味不明だ
  51. 不良:一覧表で行クリックするたびに画面を更新している
  52. 不良:配偶者のいない2つの結婚ページにそれぞれ子どもが1人づつ記入されているとき,登録では1ページに合併するはずだが,そのような動作になっていない.BASETABLEにARRAY<bnum>とlookupをそれぞれ登録されているが2ページのまま
  53. 不良:一覧表の並び替えをしただけで系図画面の再描画が発生する
  54. 不良:カードをクリックしただけで系統並び替えが発生している.⇒この動作で結婚枠オブジェクトのリサイクルが起きている.⇒これではトレース不能なので暫定的に系統並び替えを抑制
  55. 不良:リリース版をインストール時,OCXだけが上書きインストールされない
  56. 不良:ファイルを閉じようとするとき,ときどき下記のパネル「検索文字列.TXTへのアクセスが拒否されました」が出る ⇒ファイルのプロパティで見るとUsersは読み取りと実行しかできない設定になっている これはProgram Files(x86)のフォルダに書き込んでいるためと推定されるので,保管場所をドキュメントフォルダに戻すことにする インストール時「すべてのユーザ」を指定したときの動作については別途調査が必要
  57. 不良:Ancesty.zelのシンメトリが崩れてしまった⇒系列内スプリット検定の修正で起きた副作用 区間計算は人名枠ベースで行う必要がある
  58. 改善:連結線の途切れをチェックするための検査を系線グラフ検定,そのために使うグラフを系線接続関係グラフとする 座標値を保持する任意長のCPointテーブルを扱えるようにする
  59. 改善:描画オブジェクトを扱うクラスとしてDrawingObjectクラスを新設する DrawingObjectのインスタンスはTreeViewが管理する DrawingObjectクラスの中に座標対と属性を保持するテーブルを持つ
  60. 改善:人名枠氏名欄の左側に説明文を出したい できれば任意配置できるとさらによい 表示項目の範囲を広げて取捨選択できるようにしたい
  61. 改善:①対象ノードがすでに外部に実子関係を持っているときは養子関係で接続,②対象ノードを実子として登録する場合にはそのノードがすでに実子関係を持っている場合には警告パネルを出す
  62. 改善:「画面設定」メニューは「設定」とした方がよい
  63. 改善:子どもが親を2つ以上持っているとき,どちらの親の直下に配置するか指定できるとよい 
  64. 改善:純血統図では養親子関係のノードは表示しない
  65. 改善:部分図に色属性を与えて全体図の中で色別表示できるようにする
  66. 改善:NODULE,Bobject,DATALISTなどを「抽象クラス」として再定義する
  67. 改善:人名欄がイエローのとき登録ボタンで新たなカードが追加される 複数候補があるときは登録でパネルを出すべきだ また,ヒントも改善を要する
  68. 改善:マルチスクリーンで作業しているとき,別のスクリーンに出ているパネルを見落とすことがある 同じスクリーンに出した方がよい
  69. 改善:ZTでは入力ボックスの氏名とリンクすると循環が発生するような場合には,黙って新規カードを作成する⇒パネルを出した方がよい
  70. 改善:名前を付けて保存したときは,ただちにタイトルバーの表示を更新すべきだ もう一つの案として名前を付けて保存したあとも対象ファイルを切り替えないという方式も考えられる
  71. 改善:カード一覧表の表示範囲はもっと広げてよい たとえば不可視カードなど.選択されたカードというのも表示できればよいのだが… カード一覧表には表示されているカードの点数を(タイトルバーに)表示してほしい
  72. 改善:氏名同定のあいまい探索は確かに便利ではあるが,危険でもある.該当が1件の場合登録で無条件で確定するが,そのときに名前を補充している.少なくとも名前を補充した場合には,一度保留して「再登録」を待つべきだろう.氏名が厳密一致していないときはイエロー表示でもよいのではないか?
  73. 改善:系図画面では右クリックしてもメニューは出ない
  74. 改善:先祖並び替えで先祖ノードを複数選択して移動できると便利なのだが…ないし,ドラッグ移動して並び替えが実行できるとさらによい
  75. 改善:カードが削除されたとき,別のカードにジャンプする動作はあまりうれしくない.むしろ単純にその場所で再描画してもらった方がよいのだが…
  76. 改善:人名カード記入中カードを移動する場合はデフォルトで登録した方がよい.キャンセルは右クリックメニュー→再読み込みでできる
  77. 改善:カード入力中フィールドを移動するたびにIMEの文字種が(全角に)変わるのはうれしくない
  78. 改善:子ども欄に記入中ページフルになると強制登録して本人タブに移るが,次ページに移れるようにしたい
  79. 改善:InvestigateHSplits,CheckInnerSplitなどは公式版では停止する.⇒SOKUDOSAIYUSENというフラグがある
  80. 改善:可変長配列をサポートする
  81. 改善:部分図を128個まで扱えるようにする
  82. 検証:VBとC++で定数が一致していることを確認する
  83. 検証:画面上のカードを保存では生成されるファイルは誤りを含まない正規データでなくてはならない
  84. 検証:父ないし母が複数ページに存在するときの描画上の不具合,ないし,部分図で実質的に項目1のような状態になっているときの対処法
  85. 検証:サンプルは基本的にすべて読み取り専用に設定しておく必要がある
  86. 検証:MoveBundledLDRでMoveLongTailの動作をカバーできるのではないか?
  87. 宿題:先祖ノードにも結婚枠を与えるべきだった 結婚枠枠線というものがあってもよい 「先祖ノードだけの結婚枠」ないし「結婚枠の中には誰が入ってもよい」とする
  88. 参考:2019/01/11に要対処項目55件の記載がある
  89. 参考:2020/10/22「tamo2さんへの応答」に要調査項目7件の記載がある
  90. 参考:2020/10/23にグラフ処理の一般的手順リストがある
  91. 参考:regsvr32 /u DLLファイルパスでレジストリ登録解除できる
  92. 参考:部分図を使えばカードを個別に隠蔽することはできる
  93. 参考:一覧表で横スクロールバーが出る場合がある

▲構成図7.zelを閉じようとして,また「検索文字列.TXTへのアクセスが拒否されました」が出た.これはすでに解決済みではなかったのか?検索文字列.TXTというファイル名はSearchTextFileに入っている.アプリ起動時に一度読み込まれ,終了時に書き込んでいる.読み込んだときに使ったStringReaderはその場で解放している…ReadAllText,WriteAllTextはファイルのオープン・クローズなどの操作なしに読み書きできるはずなのだが…検索文字列.TXTは読み取り専用にはなっていない.⇒確かに検索文字列リストは更新されていないようだ.GetAttributesでチェックしても読み取り専用にはなっていない.

ファイルのプロパティがUserに対して解放されていなかった.Administtratorsに対してはフルコントロールを認めているが,Usersには読み取りと実行の許可しか与えられていない.templatesにはいろいろな設定ファイルが入っているが,いちいちこれでは面倒だ.置いているフォルダが悪いのだろうか?Program Files(x86)\Common Filesというのは置き場所としてはあまり適切ではないのかもしれない.ドキュメントフォルダがもっとも適切と思われるが,ここに置くと「すべてのユーザ」を設定したとき不都合かと思ったので共通フォルダを選んだのだが…「すべてのユーザ」を選択したときの複数ユーザに対する影響を調べる必要がある.

▲MoveLongTailは下流のTYW婚を1個だけ除外した移動を行う MoveBundledLDRでは下流のすべてのTYW婚を除外している MoveBundledLDRでMoveLongTailの動作をカバーできるのでは?