防御的プログラミング とは、予期しない状況下でもソフトウェアが機能し続けることを保証することを目的とした防御的設計の一形態です。防御的プログラミングの慣行は、高い可用性、安全性、セキュリティが必要な場面でよく使用されます。
防御的プログラミングは、次の点でソフトウェアとソースコードを改善するアプローチです。
- 全体的な品質 — ソフトウェアのバグや問題の数を減らす。
- ソースコードを理解可能にする — ソースコードは読みやすく理解可能であるべきで、コード監査に合格できるようにする。
- 予期しない入力やユーザー操作があっても、ソフトウェアが予測可能な方法で動作するようにする。
C++で防御的プログラミングのアプローチを採用する通常の方法は、assert機能を使用することです。
void test( int *p ) {
assert( p != 0 );
if (p == 0)
return;
// use p.
}アサーションとは、開発中に使用されるコード(通常はルーチンやマクロ)で、プログラムが実行中に自分自身をチェックできるようにするものです。アサーションが真のとき、すべてが期待どおりに動作していることを意味します。偽のときは、コード内の予期しないエラーを検出したことを意味します。
アサーションは、大規模で複雑なプログラムや高信頼性プログラムで特に有用です。プログラマーは、インターフェースの前提の不一致や、コードが変更されたときに紛れ込むエラーなどをより迅速に洗い出すことができます。
アサーションは通常2つの引数を取ります。真であるはずの前提を表すブール式と、それが真でない場合に表示するメッセージです。
新しいC++標準は、防御的プログラミングのためにどのようなメカニズムを提供しているのでしょうか?
C++11とstatic_assert
C++11では、新しいキーワードstatic_assertを使用してコンパイル時にアサーションをテストする新しい方法が導入されました。この機能は、Follyソースコードのこのテンプレートクラスに示されているように、テンプレートパラメータに条件を追加するのに非常に便利です。

C++17と[[nodiscard]]属性
で宣言された関数は、 [[nodiscard]] 呼び出し元がその戻り値を無視すべきではありません。これは、呼び出し元が戻り値をチェックすることを保証したい場合に便利です。関数のコード契約を強制して、呼び出し元が返された値をスキップしないようにできます。
例えば、do_something関数の戻り値が使用されない場合、コンパイラは警告を発します。
[[nodiscard]] error do_something (thing&);
do_something(my_thing); // Warning: ignored return valueC++20と契約(contracts)
これは、新しい標準における防御的プログラミングの大きな改善であり、契約による設計(design by contract)のための多くの機能を提供します。
提案は こちらで確認できます。
まず、契約機能の提案で使用されている用語を見てみましょう。
1. A 事前条件 is a predicate that should hold upon entry into a function. It expresses a function's expectation on its arguments and/or the state of objects that may be used by the function. Preconditions are expressed by expectsattributes (7.6.10).
2. A 事後条件 is a predicate that should hold upon exit from a function. It expresses the conditions that a function should ensure for the return value and/or the state of objects that may be used by the function. Postconditions are expressed by ensures attributes (7.6.11).
3. An アサーション is a predicate that should hold at its point in a function body. It expresses the conditions, on objects that accessible at its point in a body, that must be satisfied. Assertions are expressed by assert attributes (7.6.12).
4. Preconditions, postoconditions, and assertions are collectively called 契約. A contract shall have no observable effect in a correct program (a program where all contracts would be satisified, if they were evaluated).
5. Contract attributes are followed by a conditional-expression, which is a potentially evaluated expression (3.2).
そして、この新機能の使用例を次に示します。
void push(int x, queue & q)
[[expects: !q.full()]]
[[ensures: !q.empty()]]
{
//...
[[assert: q.is_valid()]];
//...
}C++20標準は、防御的プログラミングの実践者が求めていた機能をもたらし、契約による設計のために外部ライブラリやフレームワークを使用する必要性をなくします。
