Public API Methods with errorCode.
-
Guten Morgen Leute,
ich habe eine c library, und möchte für meine Public API eine allgmeine errorCode konzept implementieren.
Nun habe ich zwei möglichkeiten:
- Out Parameter:
typedef struct { int code :} tErrorCode; int performOperation(int input, tErrorCode*errorCode) { // do something *errorCode.code = // 0 ok otherwise error // do something return // any result }
- return mutliple return struct :
typedef struct { int code :} tErrorCode; typedef struct { int result; tErrorCode error; } OperationResult; OperationResult performOperation(int input) { OperationResult opResult; // do something opResult.error.code= // 0 ok otherwise error opResult.result = // result; // do something return opResult; }
mir persönlich gefällt variante 1 besser.. nun frage mich mich ob es noch eine weitere Möglichkeit gibt? oder welche Würde ihr nehmen vor und nachteile?
-
Unter diesen beiden: Variante 1. Du solltest auch die Möglichkeit durchdenken, dass nicht der Caller das errorCode-Struct eingibt, sondern dass der Fehlerverursacher das Objekt erzeugt. Und es ansonsten
NULL
setzt. Das macht die Benutzung sehr viel einfacher und es werden auch nur im Bedarfsfall Error-Structs erzeugt. Fehlerbehandlung ist dann auch sehr lesbar:ErrorCode *error; my_function(error); if (error) ...
Hintereinanderschalten und Schachtlung von Funktionen und Durchreichen von Fehlern ist auch einfach. Siehe beispielsweise hier, wie das in bekannten und erfolgreichen Libraries gemacht wird:
https://docs.gtk.org/glib/error-reporting.html
(und viele andere auch, denn das Konzept ist solide)Zwei Möglichkeiten will ich noch erwähnen:
- Klassisch mit globalem Error-Flag, wie mit ERRNO & Co. in der Standardbibliothek. Bin kein Fan, die Probleme sind offensichtlich. Aber es ist einfach und unsichtbar.
- Wenn du immer überall objektorientiert bist (oder wenigstens dort, wo Fehler passieren können), also deine Ergebnisse immer per Pointerargument zurück in deine "Objekte" schreibst, dann bleibt der Rückgabewert für Fehlercodes frei. Siehe beispielsweise Linux-Syscalls, die fast alle einen int zurück geben, der den Erfolg oder nicht-Erfolg der Funktion beschreibt. Nur triviale Getter-Funktionen, bei denen nichts schief gehen kann, geben direkt einen Wert zurück. Das ist auch ein solides Konzept.
Was ich auch immer im fragen würde bei der Entscheidung ist, ob und wie leicht der Anwender die Fehlerprüfung vergessen kann, und was für Konsequenzen das hätte:
- Bei einem expliziten error-Parameter kann man das gar nicht vergessen und es wäre schon sehr schlampig als Anwender, diesen nicht auszuwerten, wenn man ihn übergibt. Dafür muss man ihn aber auch immer angeben, ob man will oder nicht.
- Bei einem Rückgabewert (und erst recht ganz schlimm einem globalen Flag) kann man die Auswertung auch vergessen. Klingt erst einmal schlimm, aber sind wir doch mal ehrlich: Wie oft habt ihr schon den Rückgabewert von
printf
ausgewertet? Wusstet ihr überhaupt, dass das einen Fehlerindikator hat, der ausgewertet werden kann? Nicht alles, was theoretisch schief gehen kann, braucht man praktisch zu prüfen.
-
Ich nutze einen Ansatz ähnlich zu 1.)
typedef int tErrorCode; ///< 0 = OK, < 0 = Fehlercode #define ERR_CODE_FOO; tErrorCode Foo() { } bool Bar(tErrorCode* Err) { }
Ein Struct birgt meiner Meinung nach die Gefahr dass man weitere Elemente hinzufügt, welche sich nicht unbedingt mit der API Schnittstelle vertragen.
-
@SeppJ sagte in Public API Methods with errorCode.:
Unter diesen beiden: Variante 1. Du solltest auch die Möglichkeit durchdenken, dass nicht der Caller das errorCode-Struct eingibt, sondern dass der Fehlerverursacher das Objekt erzeugt. Und es ansonsten
NULL
setzt. Das macht die Benutzung sehr viel einfacher und es werden auch nur im Bedarfsfall Error-Structs erzeugt. Fehlerbehandlung ist dann auch sehr lesbar:ErrorCode *error; my_function(error); if (error) ...
Hintereinanderschalten und Schachtlung von Funktionen und Durchreichen von Fehlern ist auch einfach. Siehe beispielsweise hier, wie das in bekannten und erfolgreichen Libraries gemacht wird:
https://docs.gtk.org/glib/error-reporting.html
(und viele andere auch, denn das Konzept ist solide)Zwei Möglichkeiten will ich noch erwähnen:
- Klassisch mit globalem Error-Flag, wie mit ERRNO & Co. in der Standardbibliothek. Bin kein Fan, die Probleme sind offensichtlich. Aber es ist einfach und unsichtbar.
- Wenn du immer überall objektorientiert bist (oder wenigstens dort, wo Fehler passieren können), also deine Ergebnisse immer per Pointerargument zurück in deine "Objekte" schreibst, dann bleibt der Rückgabewert für Fehlercodes frei. Siehe beispielsweise Linux-Syscalls, die fast alle einen int zurück geben, der den Erfolg oder nicht-Erfolg der Funktion beschreibt. Nur triviale Getter-Funktionen, bei denen nichts schief gehen kann, geben direkt einen Wert zurück. Das ist auch ein solides Konzept.
Was ich auch immer im fragen würde bei der Entscheidung ist, ob und wie leicht der Anwender die Fehlerprüfung vergessen kann, und was für Konsequenzen das hätte:
- Bei einem expliziten error-Parameter kann man das gar nicht vergessen und es wäre schon sehr schlampig als Anwender, diesen nicht auszuwerten, wenn man ihn übergibt. Dafür muss man ihn aber auch immer angeben, ob man will oder nicht.
- Bei einem Rückgabewert (und erst recht ganz schlimm einem globalen Flag) kann man die Auswertung auch vergessen. Klingt erst einmal schlimm, aber sind wir doch mal ehrlich: Wie oft habt ihr schon den Rückgabewert von
printf
ausgewertet? Wusstet ihr überhaupt, dass das einen Fehlerindikator hat, der ausgewertet werden kann? Nicht alles, was theoretisch schief gehen kann, braucht man praktisch zu prüfen.
Hallo, danke für deine Ausführungen.
In meinem Fall "muss" der Error-Struct instanc bzw die Referenz definitiv vorhanden sein, da die Lib ein statische objekt model verwendet (kein malloc darf verwendet werden) . Ein error null ptr wird mit einem eigenes assert callback (release) geprüft. (oder ich setze einfach kein errorCode, und implementiere das selbe verhalten mit oder ohne error instanz.
Wenn methoden keinen wird zurück geben, würde ich trotzdem gern per Konvention ein error ref mitgeben... so meine Idee der API
Und ein Globales error like GetError WinApi oder so, wäre nichts für mich;)
@Quiche-Lorraine sagte in Public API Methods with errorCode.:
Ich nutze einen Ansatz ähnlich zu 1.)
typedef int tErrorCode; ///< 0 = OK, < 0 = Fehlercode #define ERR_CODE_FOO; tErrorCode Foo() { } bool Bar(tErrorCode* Err) { }
Ein Struct birgt meiner Meinung nach die Gefahr dass man weitere Elemente hinzufügt, welche sich nicht unbedingt mit der API Schnittstelle vertragen.
Ich hätte mir gern ein error struckt mit 2 integer :
typedef struct{ int modulID; int errorCode} eError;
aber klar muss mir mal die "user" perspektive anschauen, ob das ganze dann auch "schön" anwendbar ist...