契約アサーション (C++26から)
契約アサーションは、プログラムの実行中の特定の時点で成立することが期待されるプログラムの状態に関するプロパティを、プログラマが指定できるようにするものです。
目次 |
[編集] 解説
契約アサーション (Contract assertions)は、関数契約指定子とcontract_assert文によって導入されます。各契約アサーションは、bool型の式である述語 (predicate) を持ちます。
[編集] 契約アサーションの評価
契約アサーションの評価は、以下の評価セマンティクスのいずれかを使用します。
| 評価セマンティクス | チェックセマンティクスであるか | 終了セマンティクスであるか |
|---|---|---|
| ignore | ||
| observe | はい | |
| enforce | はい | はい |
| quick-enforce | はい | はい |
契約アサーションの特定の評価に対してどの評価セマンティクスが使用されるかは、処理系定義です。評価セマンティクスは、定数評価中を含め、同じ契約アサーションの異なる評価の間で異なる可能性があります。
"ignore"セマンティクスが使用された場合、契約アサーションの評価は何も効果を持ちません。
チェックセマンティクスが使用された場合、契約アサーションの評価Eは述語の値を決定します。述語が評価されるかどうかは未規定です。以下のいずれかの条件が満たされた場合、契約違反 (contract violation) が発生します。
Aより前に発生する他のいかなる操作OPもCPより前に発生するような、Eより前に発生する観測可能なチェックポイント (observable checkpoint) CPが存在する。
int num = 0; void f() pre((num++, false)); f(); // Increment of “num” might not occur, even if a checking semantic is used
[編集] 契約違反の処理
明示的な定数評価コンテキストで契約違反が発生した場合:
- 評価セマンティクスが"observe"の場合、診断メッセージが生成されます。
- 評価セマンティクスが終了セマンティクスの場合、プログラムは不適格となります。
明示的な定数評価コンテキストではない場所で契約違反が発生した場合:
- 評価セマンティクスが"quick-enforce"の場合、プログラムは契約によって終了させられます。
- 評価セマンティクスが"enforce"または"observe"の場合、契約違反に関する情報を含むconst std::contracts::contract_violation型のオブジェクトobjを参照する左辺値参照を引数として、契約違反ハンドラが呼び出されます。
- objのためのストレージは未規定の方法で割り当てられますが、グローバルな割り当て関数は呼び出されません。
- objの生存期間は、契約違反ハンドラの呼び出しが終了するまで持続します。
[編集] 契約によって終了させられたプログラム
プログラムが契約によって終了させられる (contract-terminated) とき、以下のいずれが発生するかは処理系定義です(コンテキストに依存します)。
- std::terminateが呼び出される。
- std::abortが呼び出される。
- 実行が終了する(それ以降の実行ステップは発生しない)。
[編集] 契約違反ハンドラ
プログラムの契約違反ハンドラ (contract-violation handler) は、::handle_contract_violationという名前の関数です。
| void handle_contract_violation( std::contracts::contract_violation ); |
(C++26以降) (任意でnoexcept) |
|
デフォルト契約違反ハンドラ (default contract-violation handler) と呼ばれる契約違反ハンドラの定義は、処理系によって提供されます(標準ライブラリのヘッダではなく)。
契約違反ハンドラが置換可能 (replaceable)かどうかは処理系定義です。契約違反ハンドラが置換可能でない場合、契約違反ハンドラの置換関数を宣言すると、プログラムは不適格となりますが、診断メッセージは要求されません。
契約違反ハンドラが正常にリターンした場合:
- 評価セマンティクスが"observe"の場合、制御フローは契約アサーションの評価点の直後から正常に継続します。
- 評価セマンティクスが"enforce"の場合、プログラムは契約によって終了させられます。
契約違反ハンドラが正常にリターンした後に発生する他のいかなる操作OPもCPより後に発生するような、契約違反ハンドラが正常にリターンした後に発生する観測可能なチェックポイントCPが存在します。
[編集] アサーションからの例外の処理
述語の評価が例外によって終了したために契約違反が発生し、かつ評価セマンティクスが"observe"または"enforce"である場合、契約違反ハンドラはその例外に対するアクティブな暗黙のハンドラ内から呼び出されます。
契約違反ハンドラが正常にリターンした場合:
- 評価セマンティクスが"observe"の場合、暗黙のハンドラはもはやアクティブであるとは見なされません。
- 評価セマンティクスが"enforce"の場合、契約による終了が発生したときも暗黙のハンドラはアクティブなままです。
現在の例外は、契約違反ハンドラ内でstd::current_exception()を使用して調査したり、再送出したりすることができます。
[編集] 順次評価
契約アサーションのリストRを順次評価 (evaluate in sequence) するには:
Sを構築します。Rのすべての要素がSに含まれる。Rの各要素は、S内で処理系定義の回数だけ繰り返されることがある。Rにおいて契約アサーションAが別の契約アサーションBより前にある場合、SにおいてもAの最初の出現はBの最初の出現より前にある。
void f(int i) { contract_assert(i > 0); // #1 contract_assert(i < 10); // #2 // valid sequence of evaluations: #1 #2 (no repeat) // valid sequence of evaluations: #1 #1 #2 #2 (repeat in sequence) // valid sequence of evaluations: #1 #2 #1 #2 (repeat alternatively) // valid sequence of evaluations: #1 #2 #2 #1 (second occurences can switch order) // invalid sequence of evaluations: #2 #1 (first occurences cannot switch) }
[編集] 注釈
利用可能な評価セマンティクスの選択肢の範囲と柔軟性は処理系に依存し、4つすべての評価セマンティクスを可能性として許容する必要はありません。
契約アサーションが定数式によって生成される値を変更する副作用を持つ場合、異なる翻訳単位で同じ契約アサーションに対して異なる評価セマンティクスを選択すると、ODR違反 (one-definition rule) となる可能性があります。
constexpr int f(int i) { contract_assert((++const_cast<int&>(i), true)); return i; } inline void g() { int a[f(1)]; // size dependent on the evaluation semantic of contract_assert above }
述語を評価した結果の値がtrueである場合、契約違反は発生せず、制御フローは契約アサーションの評価点の直後から正常に継続します。
述語の評価が非局所ジャンプやプログラムの終了によって終了した場合も、契約違反は発生しません。
C++標準では、デフォルトの契約違反ハンドラは、引数の最も関連性の高い内容を適切にフォーマットした診断出力を生成し(観測される契約アサーションの違反が繰り返される可能性があるためレート制限付きで)、その後正常にリターンすることが推奨されています。
| 機能テストマクロ | 値 | 規格 | 機能 |
|---|---|---|---|
__cpp_contracts |
202502L |
(C++26) | 契約プログラミング |
[編集] キーワード
[編集] 関連項目
contract_assert文 (C++26) |
実行中に内部条件を検証する |
| 関数契約指定子 (C++26) | 事前条件 (pre) と事後条件 (post) を指定する |