読者です 読者をやめる 読者になる 読者になる

C Sharpens you up

http://qiita.com/yuba に移しつつあります

Intellisenseが解析あきらめるコードもあるんです

納涼!ほんとにあった怖いコード(by CodeIQ×はてな)

ある夏体験した悪夢のようなコーディングの話です。

そのソフトは、もともと1本のプログラムでクライアントとしてもサーバとしても機能するという作りになっていました。

そのソフトに転機が訪れます。
サーバ・クライアントを別々のプログラムに分離することに、さらにはサーバ側はWindowsサービス(UNIXで言うところのデーモン)とUIプログラムに分離することに、つまり都合3本のプログラムに分けることになったのです。

担当していた開発者はそれをソース分岐ではなく、あくまで単一のソースで実現しようとしました。C++の #IFDEF マクロを使い、コンパイルオプションに従って各プログラムで必要な部分だけがコンパイルされるように、文字列リソースはそれができないのでプロジェクトごとに別ファイルを参照するようにして…

それを30プロジェクト、500クラスを超える規模のソフトウェアでやったのです。しかも、C++の場合クラス宣言とクラス実装は別ファイルですから、 #IFDEF でどこか条件付きにすると言ってもあちらで関数宣言を隠し、こちらで関数実装を隠し、使わなくなった定数をあちらで隠し、文字列リソースをこちらからだけ削除し… と大量のソースはどれも条件コンパイルだらけ。

そしてそこでもう一つのバッドプラクティスが火を噴きました。

もともと、ソース3分岐に踏み切れなかった理由があったのです。それはブランチやマージの扱えるVCSを使っていなかったせいでした。ということは、そう。改変するたびに変更をコメントで残すという習慣が固く守られていました。そして入れ子コメントのできないC++でそれをどうするかというと。 #IF 0 〜 #ELSE 〜 #ENDIF マクロを使うのです。

3つのプログラムを単一ソースから作るための #IFDEF 〜 #ELSE 〜 #ENDIF マクロ、そして変更履歴をすべて残すための #IF 0 〜 #ELSE 〜 #ENDIF マクロ。

ほとんどのソースは、中括弧よりもマクロ指令が多いという状態でした。
そして、#ELSE 〜 #ENDIF を見ただけでは、ここがすでに変更された古いコードの履歴なのか、単にこのプロジェクトでは不要だからコンパイルされない部分かまったく見分けがつきません。

そして、そんな開発に投入された私が見たのは目を疑う光景でした。
Visual StudioのIDEさえ、どこがコンパイル対象の行だか判定し損ねているのです。コンパイラは辛うじて正しく解釈してくれるのでその結果、ステップ実行を始めるとコメントと判定されたグレーの行をしゃあしゃあとステップカーソルが走って行きます。

もちろんそんなソース、まともに入力補完されるわけがありません。Visual Studioサクラエディタよりましなのはデバッガを内蔵している点だけという状態でした。

そのコードがどういう質だったかは恐ろしくて口にもできませんので、私の心が癒えた頃に、また。