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
typedefenum
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:
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:
// 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
returnerror_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:
// Muss vor return auf jeden Fall ausgeführt werden
33
SetMutex(funktion_eins_mutex);
34
returnerror_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.
Es ist faszinierend, was man alles unternimmt, um C++ und Ausnahmen zu
vermeiden.
Wenn sonst nichts wirklich ernstes dagegen spricht, würde ich einfach zu
C++ raten.
In C ist vernünftige Fehlerbehandlung wesentlich arbeitsaufwändiger,
unleserlicher und entweder auch nicht umsonst oder sehr lückenhaft und
vor allem fehleranfälliger.
Klaus Wachtler schrieb:> Es ist faszinierend, was man alles unternimmt, um C++ und Ausnahmen zu> vermeiden.
Komisch! Eine ganze Latte an aktuellen Projekten verwendet schlichtes C
bei Mikrocontrollern. Ob die den technischen Fortschritt verpennt haben?
Ich denke nicht!
Klaus Wachtler schrieb:> In C ist vernünftige Fehlerbehandlung wesentlich arbeitsaufwändiger,> unleserlicher und entweder auch nicht umsonst oder sehr lückenhaft und> vor allem fehleranfälliger.
Tja, das ist mal wieder so eine Pro-/Contra-Sache. Dafür geht er eben
den Nachteilen von C++ aus dem Weg, indem er eben jene Sprache
vermeidet. ;-)
Um kurz das auf das Thema zurückzukommen: Meine Fehlerbehandlung ist
ähnlich der der Win32 API bzw. der des TO. Überall passende Fehlercodes,
ein einheitlicher Typ für eben jene und Unterstützung mit ein paar
gescheiten Makros.
Im Grunde könnte man sagen, man hätte Exceptions in C nachgerüstet. Aber
die Sache hat doch in meinen Augen einen ernormen Vorteil:
Die Fehlerbehandlung ist sichtbar!
-> Es fliegen keine unsichtbaren Exceptions durch die Gegend.
-> Es werden nicht implizit Destruktoren aufgerufen.
-> Der Programmfluss ist exakt so wie er im Code steht.
Dass eine durchdachte Fehlerbehandlung in C fehleranfälliger ist als in
C++ wage ich stark zu bezweifeln.
dotm schrieb:> Vorallem die unsägliche goto- Anweisung
Dein Fall ist einer der wenigen, bei dem goto sinnvoll ist. Die gotos
teilweise hinter einem harmlos klingenden CHECKERROR() zu verstecken,
ist hingegen böse und wird dich den Flüchen der Menschen aussetzen, die
das irgendwann mal weiterbearbeiten müssen.
Somit ist eigentlich einer implementierung nach meinem Schema nichts
mehr auszusetzen (ausser dass ich das CHECKERROR evt weglasse).
Irgendwelche Verbesserungsvorschläge?
dotm schrieb:> typedef enum> {> ERROR = 0,> SUCCESS = !ERROR> } ErrorStatus;
Ziemlicher Humbug. Das erste Element einer Aufzählung in C hat
immer den Wert 0, wenn man nichts angibt, damit ist das "= 0"
völlig überflüssig.
!0 hat immer den Wert 1 in C, ist also genauso überflüssig …
Klaus Wachtler schrieb:> Es ist faszinierend, was man alles unternimmt, um C++ und Ausnahmen zu> vermeiden.
Man muss allerdings der Gerechtigkeit halber sagen, dass gerade
Exceptions (so schön bequem sie sein mögen) sich schnell zu
Codemonstern im generierten Code aufblähen können. Meiner Erinnerung
nach sind sie aus ebendiesem Grund aus dem Pflichtumfang von Embedded
C++ weggelassen worden.
Wenn die Ressourcen da sind, würde ich sie allerdings einer händischen
Fehlerbehandlung in C vorziehen.
Jörg Wunsch schrieb:> Ziemlicher Humbug. Das erste Element einer Aufzählung in C hat> immer den Wert 0, wenn man nichts angibt, damit ist das "= 0"> völlig überflüssig.
das ist nicht von mir sondern von ST.
dotm schrieb:> das ist nicht von mir sondern von ST.
Das ging auch nicht gegen dich, sondern gegen den, der das verzapft
hat.
Es wäre mir Grund genug, derartigen Code nicht als eigenes Vorbild
zu nehmen. Wenn jemand derartige Verrenkungen fabriziert, um Dinge
zu erreichen, die vom C-Standard klipp und klar geregelt sind, dann
ist mir das ein Hinweis, dass derjenige von der Sprache nicht gerade
viel Ahnung hat.
dotm schrieb:> Irgendwelche Verbesserungsvorschläge?
Ich würde den Fehlerstatus mit einem Wert ungleich 0 initialisieren und
erst unmittelbar vor dem Fehler-Label auf 0 setzen.
Alexander B. schrieb:> -> Es werden nicht implizit Destruktoren aufgerufen.
Die anderen Punkte kann ich nachvollziehen, aber diesen einen Punkt sehe
ich als den größten Nachteil an. Daß die aufgerufen werden, ist doch
grad die große Stärke von Exceptions in C++. Es werden ja auch nicht
willkürlich irgendwelche Destruktoren aufgerufen, sondern die von den
Objekten, deren Scope verlassen wird, die also eigentlich eh nicht mehr
exisiteren. Deren Hinterlassenschaften müssen aufgeräumt werden, und
C++ sorgt dafür, daß man das nicht vergessen kann.
Wenn ich beispielsweise in einer Funktion ein lokal angelegtes File
einlese und nach dem Öffnen der Parser eine Exception wirft, wird das
File ganz automatisch geschlossen, bevor aus der Funktion gesprungen
wird.
Tom schrieb:> dotm schrieb:>> Vorallem die unsägliche goto- Anweisung
Warum sollte die "unsäglich" sein? Nur weil das gerne mal behauptet
wird?
> Dein Fall ist einer der wenigen, bei dem goto sinnvoll ist. Die gotos> teilweise hinter einem harmlos klingenden CHECKERROR() zu verstecken,> ist hingegen böse
Das sehe ich ganz genauso. Mit goto kann man in dieser Situation den
leserlichsten und einfachsten Code produzieren. Aber daß hinter einem
Makro irgendwelche Sprünge im Programm verborgen sind, damit rechnet man
doch nicht.
Eine eigene Frage?
Dieser Thread ist über 12 Jahre alt.
Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.