"Herr, ich habe mir eine Drachenverteidigung ausgedacht." Er hat keine Angst mehr vor uns! Es wird durch das Flattern der Flügel eines Drachen ausgelöst und schaltet eine laute Sirene ein, damit jeder den Drachen näher kommen hören kann.
"Tut diese Verteidigung noch etwas?"
"Nein, warum?" Wir werden gewarnt!
„Ja ... von den heulenden Sirenen gefressen ... Und doch ... erinnern Sie uns daran, wenn wir Ausfälle geplant haben? ...
Problembeschreibung
Diese Methode gibt nicht das Konzept der Fehlerbehandlung in komplexen und komplexen Projekten vor. Es ist vielmehr ein Beispiel dafür, was mit minimalen Mitteln getan werden kann.
Eine gute Norm ist anzunehmen, dass während der Programmausführung kein assert () ausgelöst werden soll. Wenn beim Testen der Anwendung mindestens eine assert () funktioniert hat, müssen Sie diesen Fehler an den Entwickler senden. Was aber, wenn die Anwendung nicht vollständig getestet wurde? Und assert () funktioniert für den Client? Fehler an Entwickler senden? Programm abbrechen? In Wirklichkeit ist dies die Release-Version der Anwendung und der Standard assert () wird einfach deaktiviert. Die Frage stellt sich auch mit dem internen Widerspruch des Systems: Es sollte viel assert () geben, um das Erkennen von Fehlern zu erleichtern, aber assert () sollte weniger sein, um den Benutzer und seine Arbeit mit der Anwendung weniger zu unterbrechen. Ich möchte insbesondere nicht „fallen“, wenn die Anzahl der Benutzer die Anwendung von der Stabilität der Arbeit abhängt und wenn assert () im Wesentlichen unbedeutend ist (Korrektur erforderlich, aber beispielsweise recht erfolgreich weiterarbeiten kann).
Solche Überlegungen führen dazu, dass assert () c / c ++ verfeinert werden muss. Und definieren Sie Ihre eigenen Makros, die die Funktionalität von standard assert () erweitern - aber durch Hinzufügen einer minimalen Fehlerbehandlung. Lassen Sie solche Makros sein.
VERIFY_EXIT (Bedingung);
VERIFY_RETURN (Condition, ReturnValue);
VERIFY_THROW (Bedingung, Ausnahme);
VERIFY_DO (Bedingung) {/ * Fehlerblock * /};(Diese Makros können auch anders aufgerufen werden. Zum Beispiel VERIFY_OR_EXIT (), VERIFY_OR_RETURN (), VERIFY_OR_THROW (), VERIFY_OR_DO (). Oder umgekehrt in einer kürzeren Version.)
Diese Makros haben zum einen eine Implementierung sowohl für die Debug-Version der Kompilierung als auch für die Release-Version. Dadurch können sie sich in der Release-Version des Programms verhalten. Das heißt, Führen Sie Aktionen nicht nur während des Testens, sondern auch mit dem Benutzer aus.
Makrobeschreibung
(Die Beschreibung der Makros ist ungefähr, ihr anderes Design ist ebenfalls möglich.)
1)
VERIFY_EXIT (Bedingung);Es überprüft die Bedingungsbedingung und ruft, wenn sie falsch ist, die Standard-assert () (Debug-Version) auf und beendet auch die aktuelle Funktion (Debug- und Release-Versionen).
2)
VERIFY_RETURN (Bedingung, Rückgabewert);Es überprüft die Bedingungsbedingung und ruft, wenn sie falsch ist, die Standard-assert () (Debug-Version) auf und beendet die aktuelle Funktion durch Rückgabe von
ReturnValue (Debug- und Release-Versionen).
3)
VERIFY_THROW (Bedingung, Ausnahme);Überprüft die Bedingungsbedingung und wenn sie falsch ist, ruft sie die Standard-assert () (Debug-Version) auf und löst auch eine
Ausnahme aus (Debug- und Release-Versionen).
4)
VERIFY_DO (Bedingung) {/ * Fehlerblock * /};Überprüft die Bedingungsbedingung. Wenn sie falsch ist, ruft sie den Standard assert () (Debug-Version) auf und führt auch einen
Fehlerblock oder eine Operation unmittelbar nach dem Makro aus (Debug- und Release-Versionen).
Für alle Makros ist es wichtig:
- In allen Fällen muss die Bedingung wahr sein, um das Makro zu "übergeben", und falsch, um den Pfad der minimalen Fehlerbehandlung zu aktivieren.
- Jedes der Makros implementiert eine minimale Methode zur Fehlerbehandlung. Dies ist erforderlich, um das Verhalten bei Fehlern zu implementieren, die beim Testen nicht erkannt wurden, aber beim Benutzer aufgetreten sind. Abhängig von der Implementierung können Sie den Entwickler über einen Fehler informieren, der auf dem Client aufgetreten ist. Jede Implementierung bietet jedoch eine minimale Möglichkeit, einen Fehler zu beheben.
Makro-Verwendungsmuster
Das Interessanteste ist natürlich, dass die Entropie-Supermenschen (Helden der Reduzierung von Programmfehlern) die Verwendung dieser Makros sind.
1) Vor- und Nachbedingungen.
Der erste Anwendungsfall sind Vor- und Nachbedingungen. Ich möchte Sie daran erinnern, dass Vorbedingungen den Status des Programms (Eingabeargumente, Objektstatus, verwendete Variablen) auf Übereinstimmung mit den erforderlichen Anforderungen des ausgeführten Codefragments überprüfen. Post-Bedingungen (sie sind in Programmen weniger verbreitet) sollen überprüfen, ob wir das gewünschte Ergebnis erzielt haben und ob der Status der Objekte für das aktuelle Codefragment gültig bleibt.
Die Verwendung der vorgeschlagenen Makros ist unkompliziert - wir weisen jede Prüfung in einem separaten Makro zu. Wir wählen Makros basierend auf der von uns benötigten Fehlerbehandlung aus. (VERIFY_EXIT () - Fehlerverarbeitung beim Beenden dieser Funktion, VERIFY_RETURN () - Fehlerverarbeitung bei Rückgabe eines Wertes, VERRIFY_THROW () - Fehlerverarbeitung mit Ausnahme usw.)
Sie können auch das VERIFY () -Makro hinzufügen oder verwenden, das keine Fehlerbehandlung durchführt. Dies kann beispielsweise unter Nachbedingungen am Ende einer Funktion nützlich sein.
Diese Makros sind völlig autark, wenn Sie die Prinzipien des reinen Codes verwenden und genügend Funktionen zuweisen, um atomare Aktionen zu implementieren. Jede Funktion kann den Status eines Objekts, Eingabeargumente usw. überprüfen. seine atomare Wirkung auszuführen.
2) Semantik einer Transaktion.
Diese Makros können auch verwendet werden, um Code mit Transaktionssemantik zu implementieren. Eine solche Semantik wird verstanden als: 1) schrittweise Vorbereitung auf eine Operation mit Überprüfung der Ergebnisse jeder der Vorbereitungsstufen; 2) die Durchführung der Aktion nur, wenn alle Vorbereitungsphasen erfolgreich waren; 3) Verweigerung der Erfüllung, wenn einige Bedingungen in der Vorbereitungsphase nicht erfüllt sind (mit einem möglichen Rollback von der Erfüllung).
3) Entwerfen des Codes unter Berücksichtigung einer möglichen Erweiterung.
Dies gilt insbesondere für Bibliotheken und allgemeinen Code, die anfänglich im selben Kontext der Ausführungsbedingungen entwickelt werden können und später mit anderen Bedingungen verwendet werden können (beginnen, anders verwendet zu werden). In diesem Fall können diese Makros die "Grenzen" der Funktionalität des Codes beschreiben. Bestimmen Sie, was ursprünglich als Fehler angesehen wurde und was erfolgreich war. (Dieser Ansatz kommt den klassischen Pre-Post-Bedingungen sehr nahe.) Natürlich schreibe ich die „Ränder“ in Anführungszeichen, weil Diese Grenzen können überarbeitet werden, es ist jedoch wichtig, das Wissen über die akzeptablen Grenzen des Code-Designs zu ermitteln (oder vielmehr an zukünftige Entwickler weiterzugeben).
Makroimplementierung
Ich gehe davon aus, dass die meisten Entwickler auf mittlerer Ebene keine Probleme haben werden, diese Makros zu implementieren. Aber wenn Sie Informationen benötigen, werde ich einige wichtige Punkte widmen.
Makros müssen als einzelne Anweisung darstellbar sein. Was kann mit do {} while (false) Konstrukten oder ähnlichem gemacht werden? Zum Beispiel so:
#define VERFY_EXIT(cond) \ do{bool _= (bool)(cond); assert(_); if(!_) {return;}} while(false) \
Dann können Sie den folgenden Code schreiben:
if(a > 0) VERIFY_EXIT(a%2==0);
Dies ist natürlich nur eine der Möglichkeiten der Implementierung. Sie können Makros auf andere Weise implementieren.
PS Erfolgreicher Kampf mit Entropie, Übermenschen!