c++ builder > public, private, クラスでないstaticな関数の使い分け


この記事はDelphi Advent Calender 2015の16日目の記事です。

やましょうさん、お疲れ様でした。
リレータッチボードドライバなるものがあるのですね。自動化に良さそうで、トライしてみたくなりました。とりあえず注文しておきました。
ESP8266を動かせてからの次のターゲットとして検討中です。



今回はpublic, private, クラスでないstatic関数の使い分けについてです。
動作環境
C++ Builder XE4

ソースリーディングの流れ

半年前や3年前のソースを見て、バグ対応なり機能追加なりすることがありますね。
そういう時に、ソースの構造をどうしておけば、前準備であるプログラムの構造調査をすぐに終わらせることができるのか、試行錯誤中です。

以下は僕のソースリーディングの流れです。

  1. ヘッダファイル > public関数のうち外部から呼ばれるエントリ関数を調べる
  2. ヘッダファイル > public関数のうちエントリ関数以外を調べる
  3. ヘッダファイル > private関数のうちpublic関数から呼ばれるエントリ関数を調べる
  4. ヘッダファイル > private関数のうちprivate関数から呼ばれる非エントリ関数を調べる
  5. (詳細を見たい時) ソースファイル > 調査対象の関数をステップ実行などして調べていく
  6. (詳細を見たい時) ソースファイル > static関数を調べる

ここで「エントリ」の定義は「その時々で自分が見たいまとまった処理の最上位の関数」とします。

ソースリーディングのほとんどはpublic関数とprivate関数までの読込み(1から4)までで構造を理解して終わり、実際の作業に入ることが多いです。

大きなユニットファイル

  1. フォームを作ります。
  2. ButtonClick()などUIとその動作について実装します。
  3. 数値表示処理を追加します。
  4. 要求によって、データ処理も追加します。
  5. グラフを追加することになりました。
  6. ...

気づいたらユニットファイルの中にたくさんの関数ができています。
それらの関数が同列に見える場合、ソースリーディング(構造調査)の時間がかかってしまいます。
そしてその時間の消費は、「毎回そのプログラムに取り組み始めるたびにかかってしまう」ものになります。

なんとかしたいものです。

コーディングの流れ

ボトムアップとトップダウンの2種類がありますが、トップダウンの方法とします。

Code Completeでいうところの、Pseudocode Programming Process (PPP) @ Chapter9. The Pseudecode Programming Process (Kindle No.7351)で作ります。

PPPで関数を構造化したものを考えます。その際のそれぞれの関数をどこに置くのがいいか。
僕は、とりあえずprivate関数としてスタートします。

関数作成の流れ @ mindmup

そしてコーディングを進めるにつれて上記の流れのように、private関数だったものを適宜別のアクセス修飾子にしたり、別ソースに切り出しをしていきます。

private関数の処理でも、「細かいことをしすぎている」と感じる処理はクラスでないstatic関数(.cppでstatic宣言している関数)にしていきます。そのstatic関数はソースリーディングにおいて目を通さなくなるので、構造理解の邪魔にならなくなります。

static関数にする時に注意点としては「リエントラントな関数にしておく」ことです。そのstatic関数内において、static変数の書き換えや読み込みにしておくと失敗します。実際、2日前の実装で失敗して修正しました (メンバ変数の読書きにしました)。

プログラムが小さいうちは、上記の流れのうちの緑色の関数だけの構造になります。大きくなるにつれて、ほかの部分の扱いをすることになります。

なお、プログラムが6割程度までできたと感じるまでは別ソース(UtilSetting.cppなど)に分岐するのは控えています。途中段階ではよい構造が見えないというのが理由です。
6割程度までできてだいたいの構造が見えてからテストをしながらリファクタリングします。
このあたりは、ここ4年のうちに読んだ本かPDF資料で「プロジェクトがひと段落したら構造を整理しよう」と記載があったのを参考にしています。どの本だったか失念しました。

6割になる前に、ある程度の処理のグルーピングはprefixをつけて行ったりします。「これはUIまわりの処理」、や「これはxxのロジックに関する処理」など。
prefixでまとめる「この関数はどのグループに属するのか」はコーディングの中でどんどん変化していく場合もあります。そういう場合でも、1つのcppファイル、1つのヘッダファイルで扱っている間は、リファクタリングがやりやすいです。早い段階で外部クラス化してしまうと、再グルーピングに手間がかかります。

構造がある程度落ち着いた段階で、prefixでグルーピングできたものを外部クラスにまとめたりなどしていきます。

だいたいこのような感じでコーディングを進めていってます。