過去の修正を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に関わる修正は現在のものが使えるのではないかと思う.重要な書き換えはすべてヘッダファイルに入っているので,これを再利用できれば相当な利得がある.まず,そういう形で設(しつら)えてみよう.かなり手戻りだが,失敗するよりはましだ.

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA