In verschiedenen Disziplinen der Softwareentwicklung gibt es viele Philosophien darüber, wie Bibliotheken mit Fehlern oder anderen außergewöhnlichen Bedingungen umgehen sollen. Einige von denen, die ich gesehen habe:
- Gibt einen Fehlercode mit dem Ergebnis zurück, das von einem Zeigerargument zurückgegeben wird. Dies ist, was PETSc tut.
- Rückgabe von Fehlern durch einen Sentinel-Wert. Zum Beispiel gibt malloc NULL zurück, wenn es keinen Speicher zuordnen konnte,
sqrt
gibt NaN zurück, wenn Sie eine negative Zahl übergeben usw. Dieser Ansatz wird in vielen libc-Funktionen verwendet. - Ausnahmen auslösen. Wird in Deal.II, Trilinos usw. verwendet.
- Geben Sie einen Variantentyp zurück. Beispiel: Eine C ++ - Funktion, die ein Objekt vom Typ zurückgibt,
Result
wenn es ordnungsgemäß ausgeführt wird, und einen Typ verwendet, umError
zu beschreiben, wie es fehlgeschlagen ist, würde zurückkehrenstd::variant<Error, Result>
. - Verwenden Sie Assert und Crash. Wird in p4est und einigen Teilen von igraph verwendet.
Probleme bei jedem Ansatz:
- Das Überprüfen auf jeden Fehler führt zu viel zusätzlichem Code. Die Werte, in denen ein Ergebnis gespeichert wird, müssen immer zuerst deklariert werden, wodurch viele temporäre Variablen eingeführt werden, die möglicherweise nur einmal verwendet werden. Dieser Ansatz erklärt, welcher Fehler aufgetreten ist, es kann jedoch schwierig sein, festzustellen, warum oder bei einem Deep-Call-Stack wo.
- Der Fehlerfall ist leicht zu ignorieren. Darüber hinaus können viele Funktionen nicht einmal einen aussagekräftigen Sentinel-Wert haben, wenn der gesamte Bereich der Ausgabetypen ein plausibles Ergebnis ist. Viele der gleichen Probleme wie # 1.
- Nur in C ++, Python usw. möglich, nicht in C oder Fortran. Kann in C mit setjmp / longjmp Hexerei oder libunwind nachgeahmt werden .
- Nur in C ++, Rust, OCaml usw. möglich, nicht in C oder Fortran. Kann mit Makro-Zauberei in C nachgeahmt werden.
- Wohl das informativste. Wenn Sie diesen Ansatz beispielsweise für eine C-Bibliothek anwenden, für die Sie dann einen Python-Wrapper schreiben, führt ein dummer Fehler wie das Übergeben eines Index außerhalb der Grenzen an ein Array zum Absturz des Python-Interpreters.
Ein Großteil der Ratschläge im Internet zur Fehlerbehandlung wird aus Sicht von Betriebssystemen, eingebetteter Entwicklung oder Webanwendungen verfasst. Abstürze sind inakzeptabel und Sie müssen sich um die Sicherheit sorgen. Wissenschaftliche Anwendungen haben diese Probleme nicht oder nur annähernd in gleichem Maße.
Eine weitere Überlegung ist, welche Arten von Fehlern behoben werden können oder nicht. Ein Malloc-Fehler kann nicht wiederhergestellt werden, und in jedem Fall wird der Killer mit zu wenig Speicher im Betriebssystem darauf zugreifen, bevor Sie dies tun. Ein Index außerhalb der Grenzen für eine Arraygröße kann ebenfalls nicht wiederhergestellt werden. Für mich als Benutzer ist das Schönste, was eine Bibliothek tun kann, mit einer informativen Fehlermeldung abzustürzen. Andererseits könnte das Versagen beispielsweise der Konvergenz eines iterativen linearen Lösers durch Verwendung eines direkten Faktorisierungslösers behoben werden.
Wie sollten wissenschaftliche Bibliotheken Fehler melden und erwarten, dass sie behandelt werden? Mir ist natürlich klar, dass es davon abhängt, in welcher Sprache die Bibliothek implementiert ist. Aber soweit ich das beurteilen kann, möchten die Leute sie für eine ausreichend nützliche Bibliothek aus einer anderen Sprache als der, in der sie implementiert ist, aufrufen.
Abgesehen davon denke ich, dass Ansatz Nr. 5 für eine C-Bibliothek erheblich verbessert werden kann, wenn er einen globalen Assertion-Handler-Funktionszeiger als Teil der öffentlichen API definiert. Der Assertion-Handler meldet standardmäßig die Datei- / Zeilennummer und stürzt ab. Die C ++ - Bindungen für diese Bibliothek würden einen neuen Assertion-Handler definieren, der stattdessen eine C ++ - Ausnahme auslöst. Ebenso würden die Python-Bindungen einen Assertion-Handler definieren, der die CPython-API verwendet, um eine Python-Ausnahme auszulösen. Aber ich kenne keine Beispiele, die diesen Ansatz verfolgen.