Hallo!
Eventuell ist darüber schon viel geschrieben worden, ich hab mir aber
auch selbst den Kopf schon viel zerbrochen und die Lösungen aus dem
Internet passen irgendwie nicht auf meine Bedürftnisse.
Ich möchte ein Fehler/Statusbehandlungssystem in C implementieren.
Ich verwende den STM32F373 Cortex M4 mit CMISIS-RTOS.
Das erste Fehlerstatuskonstrukt das mir (vor langer Zeit) über den Weg
gelaufen ist, kam von ST:
1 | typedef enum
|
2 | {
|
3 | ERROR = 0,
|
4 | SUCCESS = !ERROR
|
5 | } ErrorStatus;
|
Prinzipiell ok, da viele Funktionen 0 als Error ausgeben, wenn man aber
bei eigenen Funktionen nie einen Wert retourniert sondern alles in
Pointer schreibt:
1 | ErrorStatus Funktion (const uint8_t eingabewert, uint8_t *ausgabewert);
|
Dann ist obige Struktur unzureichend. Grund: was für ein Fehler
aufgetreten ist, kann man nicht weiter spezifizieren.
Meine Idee war es nun den Fehler sozusagen durchzureichen. Die Methode
mit setjmp ist für mich nicht anwendbar, da einige Funktionen auf jeden
Fall deinitialisieren müssen.
Ich möchte auch die Deinitialisierungsroutinen nicht jedesmal vor einem
Return dazuschreiben, daher dachte ich an so ein Konstrukt:
1 | typedef uint16_t ErrorStatusMessage;
|
2 | #define CHECKERROR(x) if ((x) != 0) goto freturn;
|
3 | |
4 | ErrorStatusMessage FunktionEins (const uint8_t eingabewert, uint8_t *ausgabewert)
|
5 | {
|
6 | ErrorStatusMessage error_status_message = 0;
|
7 | // Initialisierungsfunktionen:
|
8 | SetMutex(funktion_eins_mutex);
|
9 |
|
10 | // Anfang Anweisungen
|
11 | error_status_message = FunktionZwei();
|
12 | CHECKERROR(error_status_message)
|
13 |
|
14 | // hier werden noch weitere Funktionen oder Anweisungen durchgeführt
|
15 |
|
16 | freturn:
|
17 | // Muss vor return auf jeden Fall ausgeführt werden
|
18 | SetMutex(funktion_eins_mutex);
|
19 | return error_status_message;
|
20 | }
|
Der Grund warum ich goto verwende und nicht break, ist dass ich auch in
einer Schleife, oder in einer switch-Anweisung zu freturn springen kann.
In dem Fall wäre nun SUCCESS = 0 und jede Fehlermeldung würde immer
komplett durchgereicht werden.
Blöderweise müsste ich dann noch sicherstellen, dass alle
Fehlermeldungen eine eindeutige Zahl haben, sonst nützt mir eine
durchgereichte Fehlermeldung recht wenig.
Ich hab mir also folgende Erweitung überlegt:
1 | #include "erroroffsets.h"
|
2 | typedef uint16_t ErrorStatusMessage;
|
3 | #define CHECKERROR(x) if ((x) != 0) goto freturn;
|
4 | #define MKERROR(x) ((x) + ERROR_OFFSET)
|
5 | |
6 | #ifdef ERROR_SPEZIALFUNKTIONEN_OFFSET
|
7 | #define ERROR_OFFSET ERROR_SPEZIALFUNKTIONEN_OFFSET
|
8 | #else
|
9 | #define ERROR_OFFSET 0
|
10 | #endif
|
11 | |
12 | #define FUNKTION_EINS_FEHLER_WERTZUKLEIN MKERROR(1)
|
13 | ErrorStatusMessage SpezialFunktionEins (const uint8_t eingabewert, uint8_t *ausgabewert)
|
14 | {
|
15 | ErrorStatusMessage error_status_message = 0;
|
16 | uint8_t wert;
|
17 | // Initialisierungsfunktionen:
|
18 | SetMutex(funktion_eins_mutex);
|
19 |
|
20 | // Anfang Anweisungen
|
21 | error_status_message = FunktionZwei (&wert);
|
22 | CHECKERROR(error_status_message)
|
23 |
|
24 | // hier wird irgendwas berechnet
|
25 | if (wert < 100)
|
26 | {
|
27 | error_status_message = FUNKTION_EINS_FEHLER_WERTZUKLEIN;
|
28 | goto freturn;
|
29 | }
|
30 |
|
31 | freturn:
|
32 | // Muss vor return auf jeden Fall ausgeführt werden
|
33 | SetMutex(funktion_eins_mutex);
|
34 | return error_status_message;
|
35 | }
|
Man kann nun in erroroffsets.h jedem modul einen offset geben:
1 | //datei erroroffsets.h
|
2 | #define ERROR_SPEZIALFUNKTIONEN_OFFSET 0x0100
|
Wenn erroroffsets korrekt gewartet ist, dann ist jede Fehlermeldung in
jedem Modul eindeutig.
Ich bin mir aber noch nicht ganz sicher ob das alles so sinnvoll ist.
Vorallem die unsägliche goto- Anweisung und das fixieren von Fehlercodes
mittels define...
Was sagt ihr dazu?
Grüsse!
M.