Vertragszusicherungen (seit C++26)
Vertragszusicherungen ermöglichen es dem Programmierer, Eigenschaften des Programmzustands anzugeben, die an bestimmten Punkten während der Ausführung gelten sollen.
Inhalt |
[edit] Erklärung
Vertragszusicherungen werden durch Funktionsvertragspezifizierer und contract_assert-Anweisungen eingeführt. Jede Vertragszusicherung hat ein Prädikat , das ein Ausdruck vom Typ bool ist.
[edit] Auswertung von Vertragszusicherungen
Eine Auswertung einer Vertragszusicherung verwendet eine der folgenden Auswertungssemantiken
| Auswertungssemantik | Prüfsemantik | Terminierende Semantik |
|---|---|---|
| ignorieren | ||
| beobachten | Ja | |
| erzwingen | Ja | Ja |
| schnell-erzwingen | Ja | Ja |
Es liegt in der Implementierungsdefinition, welche Auswertungssemantik für eine gegebene Auswertung einer Vertragszusicherung verwendet wird. Die Auswertungssemantiken können für verschiedene Auswertungen derselben Vertragszusicherung unterschiedlich sein, einschließlich Auswertungen während der konstanten Auswertung.
Wenn die Semantik "ignorieren" verwendet wird, hat die Auswertung einer Vertragszusicherung keine Auswirkung.
Wenn eine Prüfsemantik verwendet wird, bestimmt die Auswertung E einer Vertragszusicherung den Wert des Prädikats. Ob das Prädikat ausgewertet wird, ist nicht spezifiziert. Wenn eine der folgenden Bedingungen erfüllt ist, tritt eine Vertragsverletzung auf
- Der Wert, der sich aus der Auswertung des Prädikats ergeben würde, ist false.
- Die Auswertung des Prädikats endet über eine Ausnahme.
- Die Auswertung des Prädikats erfolgt in einem Kontext, der manifestiert konstant ausgewertet ist und das Prädikat keine Kern-Konstantausdruck ist.
Es gibt einen beobachtbaren Kontrollpunkt CP, der vor E liegt, so dass jede andere Operation OP, die vor A stattfindet, auch vor CP stattfindet.
int num = 0; void f() pre((num++, false)); f(); // Increment of “num” might not occur, even if a checking semantic is used
[edit] Behandlung von Vertragsverletzungen
Wenn eine Vertragsverletzung in einem Kontext auftritt, der manifestiert konstant ausgewertet wird
- Wenn die Auswertungssemantik "beobachten" ist, wird eine Diagnose ausgegeben.
- Wenn die Auswertungssemantik eine terminierende Semantik ist, ist das Programm schlecht formuliert.
Wenn eine Vertragsverletzung in einem Kontext auftritt, der nicht manifestiert konstant ausgewertet wird
- Wenn die Auswertungssemantik "schnell-erzwingen" ist, wird das Programm mit einem Vertrag beendet.
- Wenn die Auswertungssemantik "erzwingen" oder "beobachten" ist, wird die Vertragsverletzungsbehandlungsroutine mit einem L-Wert aufgerufen, der auf ein Objekt obj vom Typ const std::contracts::contract_violation verweist, das Informationen über die Vertragsverletzung enthält.
- Der Speicher für obj wird auf nicht spezifizierte Weise zugewiesen, aber es wird keine globale Allokationsfunktion aufgerufen.
- Die Lebensdauer von obj dauert für die Dauer des Aufrufs der Vertragsverletzungsbehandlungsroutine an.
[edit] Vertragsbeendete Programme
Wenn das Programm vertragsbeendet ist, liegt es in der Implementierungsdefinition (abhängig vom Kontext), ob
- std::terminate aufgerufen wird,
- std::abort aufgerufen wird, oder
- die Ausführung beendet wird (keine weiteren Ausführungsschritte erfolgen).
[edit] Vertragsverletzungsbehandlungsroutine
Die Vertragsverletzungsbehandlungsroutine eines Programms ist eine Funktion namens ::handle_contract_violation
| void handle_contract_violation( std::contracts::contract_violation ); |
(seit C++26) (optional noexcept) |
|
Eine Definition der Vertragsverletzungsbehandlungsroutine, die als Standard-Vertragsverletzungsbehandlungsroutine bezeichnet wird, wird von der Implementierung bereitgestellt (anstelle einer Standardbibliotheksüberschrift).
Es liegt in der Implementierungsdefinition, ob die Vertragsverletzungsbehandlungsroutine ersetzbar ist. Wenn die Vertragsverletzungsbehandlungsroutine nicht ersetzbar ist, ist eine Deklaration einer Ersetzungsfunktion für die Vertragsverletzungsbehandlungsroutine schlecht formuliert, keine Diagnose erforderlich.
Wenn die Vertragsverletzungsbehandlungsroutine normal zurückkehrt
- Wenn die Auswertungssemantik "beobachten" ist, wird der Kontrollfluss nach dem Auswertungspunkt der Vertragszusicherung normal fortgesetzt.
- Wenn die Auswertungssemantik "erzwingen" ist, wird das Programm mit einem Vertrag beendet.
Es gibt einen beobachtbaren Kontrollpunkt CP, der nach der normalen Rückkehr der Vertragsverletzungsbehandlungsroutine liegt, so dass jede andere Operation OP, die nach der Rückkehr der Vertragsverletzungsbehandlungsroutine stattfindet, auch nach CP stattfindet.
[edit] Behandlung von Ausnahmen von Zusicherungen
Wenn die Vertragsverletzung dadurch aufgetreten ist, dass die Auswertung des Prädikats über eine Ausnahme beendet wurde und die Auswertungssemantik "beobachten" oder "erzwingen" ist, wird die Vertragsverletzungsbehandlungsroutine innerhalb eines aktiven impliziten Handlers für diese Ausnahme aufgerufen.
Wenn die Vertragsverletzungsbehandlungsroutine normal zurückkehrt
- Wenn die Auswertungssemantik "beobachten" ist, wird der implizite Handler nicht mehr als aktiv betrachtet.
- Wenn die Auswertungssemantik "erzwingen" ist, bleibt der implizite Handler aktiv, wenn die Vertragsbeendigung eintritt.
Die aktuelle Ausnahme kann innerhalb der Vertragsverletzungsbehandlungsroutine mit std::current_exception() inspiziert oder erneut ausgelöst werden.
[edit] Sequentielle Auswertung
Um eine Liste R von Vertragszusicherungen sequentiell auszuwerten
S, so dass alle folgenden Bedingungen erfüllt sind- Alle Elemente von
Rsind inSenthalten. - Jedes Element von
Rkann eine von der Implementierung definierte Anzahl von Malen innerhalb vonSwiederholt werden. - Wenn eine Vertragszusicherung
Aeiner anderen VertragszusicherungBinRvorausgeht, dann geht die erste Instanz vonAder ersten Instanz vonBinSvoraus.
S so aus, dass, wenn eine Vertragszusicherung A einer Vertragszusicherung B in S vorausgeht, die Auswertung von A sequenziert vor der Auswertung von B erfolgt.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) }
[edit] Anmerkungen
Der Bereich und die Flexibilität der verfügbaren Auswahlmöglichkeiten für Auswertungssemantiken hängen von der Implementierung ab und müssen nicht alle vier Auswertungssemantiken als Möglichkeiten zulassen.
Unterschiedliche Auswertungssemantiken, die für dieselbe Vertragszusicherung in verschiedenen Übersetzungseinheiten gewählt werden, können zu Verstößen gegen die Ein-Definitions-Regel führen, wenn eine Vertragszusicherung Nebeneffekte hat, die den von einem konstanten Ausdruck produzierten Wert verändern.
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 }
Wenn der Wert, der sich aus der Auswertung des Prädikats ergeben würde, true ist, tritt keine Vertragsverletzung auf und der Kontrollfluss wird nach dem Auswertungspunkt der Vertragszusicherung normal fortgesetzt.
Wenn die Auswertung des Prädikats mittels nicht-lokaler Sprünge oder der Beendigung des Programms beendet wird, tritt ebenfalls keine Vertragsverletzung auf.
Der C++-Standard empfiehlt, dass die Standard-Vertragsverletzungsbehandlungsroutine diagnostische Ausgaben erzeugt, die die relevantesten Inhalte des Arguments (ratenbegrenzt für potenziell wiederholte Verletzungen beobachteter Vertragszusicherungen) zweckmäßig formatiert und dann normal zurückkehrt.
| Feature-Testmakro | Wert | Std | Feature |
|---|---|---|---|
__cpp_contracts |
202502L |
(C++26) | Verträge |
[edit] Schlüsselwörter
[edit] Siehe auch
contract_assert-Anweisung (C++26) |
verifiziert eine interne Bedingung während der Ausführung |
| Funktionsvertragspezifizierer (C++26) | spezifiziert Vorbedingungen (pre) und Nachbedingungen (post) |