最近、C++でコーディングする仕事に従事した。開発チームのルールとしてコメントの書き方が定められているが「@briefには短い説明を書く」というような文言しか無い。守りやすくわかりやすいという面では良いルールだが、長い目で見て大切なのは、どんな説明を書くべきか、どんな説明なら良いのか?だと思う。なので、コメントには何を書くべきなのか?あらためて考えてみた。以降はその結果である。
ソースコードコメントには何を書くと良いのか?
C++でコーディングする時は次のどれかを作っている。
- 他に機能を提供するコンポーネント(※1)
- 他のコンポーネント(※1)を使って機能を実現するコンポーネント(※1)
(※1)ここでは、コンポーネントはクラスや関数を指す。
大抵のコンポーネントはどちらか一方ではなく両方の性質を持つが、ここでポイントにしたいのは、大抵のコンポーネントは他に機能を提供するという点である。特に仕事としてコーディングする場合、何を提供するのか他の人にきちんと説明することが望まれる。
きちんと説明するためには、設計書などの資料を書いたりソースコードのコメントに説明を書いたりする。ここでは、ソースコードのコメントを使って他の人のためになる説明を書く方法についてまとめておきたい。
ソースコードに書く説明は、他の人がどう関わるかによって大きく二つにわけられると思う。
- コンポーネントを利用する人への説明(ここではAPI説明と呼ぶことにする)
- コンポーネントをメンテナンスする人への説明(ここではメンテナンス説明と呼ぶことにする)
API説明
API説明では、コンポーネントの使い方を正確に適切な量で読みやすく書くことが理想である。説明の量が多いと大切な説明が埋もれてしまったり、読む気をなくさえるかもしれない。これらのうち、二つはdoxygenというツールを使うことで解決させられる。doxygenはソースコードを解析してHTML形式のドキュメントに加工してくれるツールである。正確性と読みやすさがある程度確保できる。
API説明を適切な量に抑えるためには、最初からすべての説明を書ききろうとするのではなく、利用者が最も知りたいことを優先して書き始め、プログラムがバグ修正を経て成長するのと同様に、説明も他人の意見を参考にしながら成長させれば良いと考える。
利用者が最も知りたいことを説明する際、Design by Contrac(契約による設計)とか、Contract programming(契約によるプログラミング)というContractという視点を参考にしたい。
Design by ContracやContract programmingにおけるContractでは、次の三つの観点があるとされている。
- 事前条件:引数やグローバル変数、環境変数、共有メモリ等APIを提供するコンポーネント使用するコンピュータリソースに関する注意事項
- 事後条件:関数の戻り値やAPIを呼び出した後にコンポーネントが変更を加えるコンピュータリソースに関する注意事項
- 不変条件:コンポーネントが設計通りの機能を提供する上での注意事項
例えば、氏名を与えると電話番号を返す電話帳クラスを例に考えてみる。例えばContractは次のようなイメージである。
- 事前条件
- 引数の氏名は全角漢字ひらがなカタカナでなければならない。
- 引数の氏名は省略できない
- 引数の誕生日は省略することができる
- 事後条件
- 戻り値の電話番号は一人あたり0以上でありひとつだけとは限らない
- 不変条件
- 電話帳データベースが存在していなければならない
不変条件で「実行する計算機は日本に存在しなければならない」などという、多くの場面で当たり前のことは説明する必要がないが、何を優先して書くか誰が見ても納得の優先順を決めるのは難しく、先述したように一気に完成させると考えるのではなく、他の人の意見を取り入れつつ改善するのが良い。
API説明を書くコツとしては、Contract、事前条件/事後条件/不変条件を念頭に書くのが理想であるが、最初は筆が進まないかもしれない。そこで、「C++のためのAPIデザイン」マーティン・レディ著、ホジソンますみ訳が良いヒントになった。
- クラスは何者か?何を表現するものか?
- 入力値の範囲は?単位は何か?
- 戻り値の範囲は?単位は何か?
- APIが内部でチェックするエラーは何か?
- 例外を投げるか?
- スレッドセーフか?
- 未実装やTODOはあるか?
- メンバ関数の呼び出し順に制約はあるか?
- どのコンピュータリソースを参照・変更するか?
javadocやQtのドキュメントも良いお手本になった。
メンテナンス説明
コンポーネントを作成したエンジニアがずっと一人でメンテナンスすることはまず無い。コンポーネントは複数の人がメンテナンスするものである。メンテナンスする人たちの間で設計方針や試験方針を共有した上でソースコードに手を加えなければ、潜在的なバグを混入させたりコンポーネントを劣化させてしまう。
ゆえに、ソースコードにはAPI説明だけでなく、コンポーネントの構造やどういう方針の元作られているのか説明しておくことが理想である。メンテナンス説明を書くことにしたら、ソースコードレビューは、主にメンテナンス説明の方だけでよくなる。
決して、API説明にメンテナンス説明を混入させてはならない。この両者は分ける必要がある。そのため、私は宣言ファイル(.h)にAPI説明、定義ファイル(.cpp)にメンテナンス説明を書くようにしている。ただ、これはdoxygenをうまく使えばもっといいやり方があるかもしれない。
メンテナンス説明には次のような観点がある。すべてをソースコードの中に説明するのは難しいかもしれないが、こういう情報を共有して残しておくとコンポーネントのメンテナンスで問題が起きにくい。
- コーディング規約は何を適用しているのか?
- 命名規約は?
- 用語、略称、ソースコードで使用する英語
- doxygenやgcc等、ツールやコンパイラの使い方は?
- cmakeやmakefileの書き方は?
- 各ディレクトリにはどういうファイルを置くのか?
- 大きな枠組みとしてMVCか?他のアーキテクチャパターンか?
- 使用するライブラリは?ログは自作か?それともサードパーティ製を使用するのか?
- エラーログは?ワーニングログは?デバッグログは?
- レイヤ構造は?コンポーネントのパッケージ化と依存関係は?
- ユニットテストは?CIは?
- Pimplイディオムを積極的に使うか否か
以上