Mir ist klar, dass die Frage zwar kein Sprachkennzeichen hat, aber wahrscheinlich implizit von „Kaffeesprachen“ handelt. Der Vollständigkeit halber möchte ich jedoch den etwas divergierenden offensichtlichen Konsens in der C ++ - Welt erwähnen.
Es gibt drei Dinge, die C ++ - Programmierer normalerweise interessieren:
- Wird es bei optimierten Builds keinen Overhead geben? (Das heißt, kann es "kompiliert" werden?)
- Kann ich damit genau an der Stelle, an der der Fehler festgestellt wurde, einen Debugger einfangen?
- Kann ich damit Probleme von deklarierten Funktionen melden
noexcept
?
In der Vergangenheit habe ich das erste Problem gelöst, indem ich Code wie diesen geschrieben habe
int
factorial(const int n)
{
if (CHECK_ARGS)
{
if (n < 0)
throw std::invalid_argument {"n < 0"};
}
int fac = 1;
for (int i = 2; i <= n; ++i)
fac *= i;
return fac;
}
Dabei CHECK_ARGS
ist #define
d eine Konstante für die Kompilierungszeit, damit der Compiler den gesamten Code für die Argumentprüfung in optimierten Builds vollständig eliminieren kann. (Ich sage nicht, dass das Kompilieren der Checks im Allgemeinen eine gute Sache ist, aber ich glaube, dass ein Benutzer die Option haben sollte , sie zu kompilieren.)
Ich mag immer noch an dieser Lösung, dass der Code zur Überprüfung des Arguments deutlich sichtbar ist und sich in der gruppiert if
. Das zweite und dritte Problem werden dadurch jedoch nicht gelöst. Daher neige ich jetzt wieder mehr dazu, ein assert
Makro zur Argumentprüfung zu verwenden.
Die Boost-Codierungsstandards stimmen dem zu:
Was ist mit Programmierfehlern?
Wenn ich als Entwickler gegen eine Vorbedingung einer Bibliothek verstoßen habe, die ich verwende, möchte ich nicht, dass sich der Stapel auflöst. Was ich möchte, ist ein Core-Dump oder ähnliches - eine Möglichkeit, den Status des Programms genau an dem Punkt zu überprüfen, an dem das Problem erkannt wurde. Das heißt normalerweise assert()
oder so ähnlich.
Auf der CppCon'14 hielt John Lakos einen sehr interessanten Vortrag mit dem Titel Defensive Programming Done Right ( Teil 1 , Teil 2) ). Im ersten Teil seines Vortrags diskutiert er die Theorie der Verträge und des undefinierten Verhaltens. Im zweiten Teil stellt er einen meiner Meinung nach sehr guten Vorschlag zur systematischen Argumentationsprüfung vor. Im Wesentlichen schlägt er Assertion-Makros vor, mit denen der Benutzer auswählen kann, wie viel eines Budgets (in Bezug auf die CPU-Auslastung) er der Bibliothek zur Überprüfung von Argumenten spenden möchte, und die Bibliothek veranlasst, dieses Budget sinnvoll zu nutzen. Zusätzlich kann der Benutzer eine globale Fehlerbehandlungsfunktion installieren, die aufgerufen wird, wenn ein defekter Vertrag festgestellt wird.
In Bezug auf den Aspekt, dass eine Funktion privat ist, glaube ich nicht, dass dies bedeutet, dass wir sie niemals ihre Argumente überprüfen lassen sollten . Wir können uns eher darauf verlassen, dass unser eigener Code den Vertrag einer internen Funktion nicht verletzt, aber wir wissen auch, dass wir auch nicht perfekt sind. Das Überprüfen von Argumenten in internen Funktionen ist für das Erkennen eigener Fehler genauso hilfreich wie für öffentliche Funktionen zum Erkennen von Fehlern im Client-Code.