コンテンツへスキップ
Topos Uranos

要約

このレッスンは第 8 単元を締めくくり、それとともにコースを締めくくる。単元の三つのレッスンで達成したことをまとめ、モジュールのインタフェースから方法としてのデバッグへと至る概念の連鎖を組み立て直し、そのうちどの言語でも通用することを C++ に固有のことから分け、誰も診断しない単一定義規則の違反を示し、コースのすべての未定義動作を一覧にまとめて、それぞれを何が検出するかを添える。まとめテストでは導入テストの技能を繰り返し、単元を組み合わせる六つの挑戦課題を出し、最後にコースの最終プロジェクトで終わる。プロジェクトは正確に仕様が定められ、ルーブリックを備え、四つの段階からなり、各段階には検証済みの参考解答がある。

学習目標

  1. プログラムをヘッダ、インクルードガード、契約を備えた複数のモジュールに編成し、分割コンパイルし、リンカのメッセージを診断する。
  2. モジュールを仕様からテストする。サニタイザの下で実行する自動テストのプログラムを書き、起こりえないことをアサーションで確かめる。
  3. 方法(再現する、縮小する、仮説を立てる、二分探索で絞り込む)とデバッガによって欠陥の場所を突き止め、調査を記録する。
  4. コースで示した未定義動作と、それぞれを何が検出するか、そして誰も診断しない単一定義規則の違反を見分ける。
  5. コース全体を一つのプロジェクトに統合する。表形式のデータを管理するプログラムについて、仕様を定め、分解し、構築し、テストし、コストを測り、デバッグする。

単元で達成したこと

この単元は、もはや一つのファイルにも頭の中にも収まらないプログラムを編成し、検証し、デバッグすることを教えようとした。その目的は、導入が既知とした事柄、すなわち宣言と定義、コンパイルとリンク、契約、不変条件、二分探索に支えられて果たされた。単元の冒頭の書籍目録のプログラムは、一つのファイルに収まっていたが、parseInt をまた一度コピーし、仕様が「両端を含む」と述べているところに < と書いたために『百年の孤独』を黙って落としていた。このプログラムは、このレッスンの最後の節で最終プロジェクトとして書き直される。契約を備えた四つのモジュール、コマンドに応答するメインプログラム、そして二十五のケースからなるテストプログラムである。そのうち二つのケース、「範囲、境界」と「範囲、一つの年」は元のプログラムの条件では失敗するので、この誤りが誰にも気づかれずに戻ってくることはない。

複数ファイルと自作ライブラリ

レッスン複数ファイルと自作ライブラリは、プログラムを、宣言するヘッダ、定義する実装ファイル、それを使う main.cpp に分けた。一つの翻訳単位の中での二重インクルードを防ぐインクルードガード、「multiple definition」と「undefined reference」を説明し inline に許可という現在の意味を与える単一定義規則、ソースかヘッダが変わったオブジェクトだけを再コンパイルする -c による分割コンパイル、そして以前のレッスンの関数を集めた自作ライブラリを示した。

プログラムのテスト

レッスンプログラムのテストは、次のことを教えた。仕様から、領域、境界、特殊な場合ごとにケースを導き、期待される結果を別の道筋で計算すること。失敗だけを報告し、終了コード 0 か 1 で終わる自動テストのプログラムを書くこと。起こりえないことに assert を使い、-DNDEBUG がそれを条件ごと消すと承知しておくこと。static_assert と constexpr でコンパイル時にテストすること。そして、サニタイザ(メモリエラーのサニタイザ AddressSanitizer と、未定義動作のサニタイザ UndefinedBehaviorSanitizer)の下でテストを実行すること。そのすべてがダイクストラの警告に基づいている。テストはエラーの存在を示すが、その不在は決して示さない。

プログラムのデバッグ

レッスンプログラムのデバッグは、デバッグを実験的な方法として示した。故障を再現し、反証可能な仮説を立て、実験によって仮説の間で決着をつけ、疑わしい範囲を二分探索で絞り込む。道具(最適化なしの -g、三つのシステムでの Visual Studio Code の設定、実際の GDB のセッション、単純なブレークポイントと条件付きブレークポイント、ステップ実行とコールスタック)と、それよりも価値のある戦略を展開した。ケースを縮小すること、アサーションで最後の正しい点を探すこと、サニタイザのレポートで最初の自分のフレームから始めることである。