wake-up-neo.net

Was sind Verträge (wie für C ++ 17 vorgeschlagen)?

Ich las über Verträge in Gedanken zu C++ 17 von B. Stroustrup und assistierte einer kleinen Präsentation, in der ich über sie sprach, aber ich bin nicht sicher, ob ich sie wirklich verstanden habe.

Also habe ich ein paar Fragen und wenn es möglich ist, sie mit einigen Beispielen zu veranschaulichen:

  • Sind Verträge nur ein besserer Ersatz für die klassische assert() und sollten sie zusammen verwendet werden? Welche Verträge werden für einen Softwareentwickler wirklich einfach ausgedrückt?

  • Würden Verträge einen Einfluss darauf haben, wie wir mit Ausnahmen umgehen? Wenn ja, wie sollen wir Ausnahmen und Verträge verwenden?

  • Würde die Verwendung von Verträgen einen Mehraufwand zur Ausführungszeit bedeuten? Können wir sie im Release-Code deaktivieren?

Aus dem Vorschlag N4415 :

Ein Vorbedingungsvertrag des Indexierungsoperators einer Vector-Klasse könnte geschrieben werden:
T& operator[](size_t i) [[expects: i < size()]];

Ebenso könnte ein Post-Condition-Vertrag für einen Konstruktor einer ArrayView-Klasse wie folgt ausgedrückt werden: ArrayView(const vector<T>& v) [[ensures: data() == v.data()]];

Vielen Dank an @ Keith Thompson Kommentar:

Verträge haben es nicht in C++ 20 geschafft . Eine neue Studiengruppe, SG21, wurde erstellt.

34
coincoin

Soweit ich aus diesem Dokument gelesen habe: http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4415.pdf

Verträge tun, was assert seit Jahren primitiv versucht. Sie sind sowohl eine Dokumentation als auch eine Laufzeitangabe darüber, wie der Aufrufer die Funktion aufrufen soll und in welchem ​​Zustand der Aufrufer den Code erwarten kann, nachdem die Funktion zurückgegeben wurde. Diese werden üblicherweise als Vor- und Nachbedingungen oder Invarianten bezeichnet.

Dies hilft dabei, den Code auf der Implementierungsseite zu bereinigen, da wir bei Verträgen davon ausgehen können, dass Ihre Argumente, sobald die Ausführung innerhalb Ihrer Funktion erfolgt ist, einen gültigen Status haben (wie Sie es erwarten).

Der Teil "Post-Conditions" ändert möglicherweise die Art und Weise, wie Sie mit Ausnahmen umgehen, da Sie bei Verträgen sicherstellen müssen, dass das Auslösen einer Ausnahme Ihre Post-Conditions nicht beeinträchtigt. Dies bedeutet normalerweise, dass Ihr Code ausnahmesicher sein muss. Ob dies jedoch eine starke Ausnahmegarantie oder eine grundlegende Garantie bedeutet, hängt von Ihren Bedingungen ab.

Beispiel:

class Data; 
 class MyVector {
 public: 
 void MyVector :: Push_back (Elem e) [[gewährleistet: data! = nullptr]] 
 {
 if (Größe> = Kapazität) 
 {
 Data * p = data; 
 data = nullptr; // Nur aus Gründen des Beispiels ... 
 Data = new Data [capacity * 2]; // Kann eine Ausnahme auslösen 
 // Kopiere p in Daten und lösche p 
} 
 // Füge das Element am Ende hinzu 
} 
 privat: 
 Daten * Daten; 
 // andere Daten 
};

In diesem Beispiel wird die Nachbedingung verletzt, wenn der Konstruktor von new oder Data eine Ausnahme auslöst. Dies bedeutet, dass Sie diesen gesamten Code ändern sollten, um sicherzustellen, dass Ihr Vertrag niemals verletzt wird!

Natürlich können Verträge, genau wie assert, einen Laufzeit-Overhead beinhalten. Der Unterschied besteht jedoch darin, dass der Compiler, da Verträge als Teil der Funktionsdeklaration abgelegt werden können, bessere Optimierungen vornehmen kann, z. In Abschnitt 1.5 des Dokuments, das zu Beginn dieses Beitrags erwähnt wurde, werden die Möglichkeiten zum Deaktivieren von Verträgen in Abhängigkeit von Ihrer Build-Konfiguration erläutert, genau wie bei normalen alten Behauptungen.

24
KABoissonneault

Ich habe mit dem link aus dem Originaldokument das OP zur Verfügung gestellt. Ich nehme an, es gibt einige Antworten. Ich empfehle dringend, mit diesem Papier zu beginnen. Hier ist TL & DR-Version:

Verträge sind weder ein allgemeiner Mechanismus zur Fehlerberichterstattung noch ersetzen sie Test-Frameworks. Sie bieten vielmehr eine grundlegende Abhilfemaßnahme, wenn ein Programm aufgrund von nicht übereinstimmenden Erwartungen zwischen Programmteilen schief läuft. Verträge sind konzeptionell eher strukturierte Asserts (), die in die Sprache integriert sind und die die Regeln der Sprachsemantik einhalten - daher Grundlage für eine prinzipielle Programmanalyse und -tooling.

Zu Ihren Fragen:

  • Es ist strukturiert assert (), also kann man sagen, dass assert () in einigen Fällen durch contract ersetzt werden muss.
  • Lassen Sie mich hier ein anderes Zitat verwenden:

... Der Ausdruck eines Vertrages muss logischerweise Teil der Erklärung der Operation sein.

und Beispiel:

T& operator[](size_t i) [[expects: i < size()]];

Meiner Meinung nach ist das nur schön und lesbar.

  • In einigen Fällen können Verträge Ausnahmen ersetzen:

Es ist jedoch ein kritisches Entwurfskriterium, dass Verträge in eingebetteten Systemen oder anderen Systemen mit eingeschränkten Ressourcen verwendet werden können, die sich keine Ausnahmen leisten können.

In vorbedingten Verträgen können weiterhin Ausnahmen verwendet werden, da ein weiteres Verhalten nach dem Scheitern des vorbedingten Vertrages nicht garantiert ist.

  • Der Overhead kann reduziert werden, indem die Prüfung von Verträgen in folgenden Fällen aktiviert/deaktiviert wird: Alle verwenden, Nicht verwenden, Nur Vorbedingung, Nur Nachbedingung. Eingeschaltete Verträge verursachen definitiv zusätzlichen Aufwand, wie jede Art von Schecks.

Einige Anwendungsfälle (wie ich annehmen kann, obwohl ich nicht einmal in der Nähe bin, ein Vertragsdesign zu entwickeln)

  1. Verträge - im Falle von assert() als Verträge sind besser lesbar und können zum Zeitpunkt der Kompilierung optimiert werden.
  2. Asserts - in Unit-Tests, Test-Frameworks etc.
  3. Ausnahmen - können mit vorkonditionierten Verträgen verwendet werden, wie im Artikel erwähnt:

Die Vorbedingung einer Operation wird vor jeder anderen Anweisung im Funktionskörper ausgewertet. Wenn das Ergebnis wahr ist, fährt die normale Ausführungskontrolle mit der ersten Anweisung im Hauptteil der Funktion fort. Andernfalls kann die weitere Ausführung nicht garantiert werden: Entweder bricht das Programm ab oder löst eine Ausnahme aus, oder wenn es fortgesetzt werden darf, ist das Verhalten undefiniert.

Es gibt auch einigeandere Vorschläge zur Vertragsabwicklung, so dass unsere Untersuchung nur verfrüht ist.

9
Pavel Oganesyan

Es ist nicht einfach, Ihre Fragen zu beantworten, außer mit: Es kommt darauf an. Dies liegt daran, dass noch nicht klar ist, welche Verträge genau abgeschlossen werden sollen. Derzeit gibt es mehrere Vorschläge und Ideen:

  • n4378 Lakos et al. schlägt grundsätzlich vor, ein ausgeklügeltes Assert-Toolkit zu standardisieren. Verträge werden innerhalb einer Funktionsimplementierung geprüft, 3 verschiedene Assert-Ebenen werden bereitgestellt, um die Anzahl der Laufzeitprüfungen zu steuern, und die Behandlung von Assert-Verstößen kann angepasst werden.

  • n4415 dos Reis et al. und n4435 Brown sind ziemlich ähnlich und schlagen eine attributbasierte Syntax vor, um Vor- und Nachbedingungen in den Funktionsschnittstellen zu definieren. Sie gehen nicht auf Details ein, wie viel Kontrolle sie über Laufzeitprüfungen und das Verhalten bei Verstößen geben.

Es gab auch weniger aktuelle Artikel zu diesem Thema. Es gibt viele Details, über die noch nicht entschieden wurde, und diese Funktion berührt viele verschiedene Bereiche (z. B. Module, Optimierung, Erstellen/Verknüpfen), über die der Standard zum Teil wenig Kontrolle hat.

Ihre Frage zu Ausnahmen ist besonders schwierig, da die Wechselwirkungen zwischen der Behandlung von Vertragsverletzungen und Ausnahmen unklar sind (z. B. könnte ein Handler für Vertragsverletzungen (nützlich in Testframeworks)? Was ist, wenn die Funktion noexcept(true) ist?).

3
Fabio Fracassi