Hallo,
ich verwende das cleanup-Attribut des gcc um eine Interruptsperre bei
verlassen des Scopes einer (Sperr)Variable wieder aufzuheben. Soweit
funktioniert das auch. Wenn ich aber in einer Unterfunktion einen
longjump() aufrufe, wird die Sperre nicht aufgehoben, da der Scope der
Variable vermeitlich noch nicht verlassen ist. Gibt es eine Möglichkeit
dem gcc mitzuteilen, dass eine Funktion zum verlassen des Scopes führt?
__attribute__((noreturn)) führt nicht zum gewünschten Ergebnis.
Gruß
Mit Destruktoren und Exceptions (statt longjmp) in C++ geht das.
Erfordert aber eine Menge Overhead. Ich würde longjmp soweit irgends
möglich vermeiden, und Destruktoren statt Compiler spezifischer
Attribute verwenden.
C++ Exceptions sind für µC doch ziemlich fett (Du schreibst es ja). Ich
habe mit setjmp/longjump eine einfache Exceptionbehandlung in C
implementiert. Es geht mir darum, Fehler (nicht irgendwelche Zustände
wie z.B. in Java geläufig) auf jeden Fall zu behandeln. Über
Rückgabewerte ist das Ganze nur bedingt zuverlässig, da man ja weiss,
dass die Funktion immer funktioniert und der Wert dann auch nicht zu
prüfen ist ;-)
Und jetzt bin auch das Problem im Zusammenhang mit longjmp gestossen.
Den Komfort/Sicherheit welcher durch die "cleanup-Locks" erreicht wird,
möchte ich nicht wieder missen.
Das Problem ist, dass man zum automatischen Bereinigen beim
Herausspringen eine Menge Technik braucht - "personality functions",
Exceptios Tabellen, Speicher Pools... Das gleiche was man halt auch bei
Exceptions macht. Bei uC muss man da wohl in den sauren Apfel beißen und
es händisch machen. Da hat man aber sowieso meistens keine derart tiefen
Aufruf Stacks. Das Cleanup kann man mit Destruktoren oder teilweise auch
Lambdas in C++ automatisieren, aber longjmp bringt einfach alles
durcheinander...
Das ist m.E. ein rein logisches Problem, kein Problem der Sprache. Auch
C++-Exceptions und/oder Destruktoren können das nur verstecken, nicht
beheben.
Wenn Du einen Mechanismus hast, der lediglich bei einem (geordneten)
Verlassen einer Routine einen globalen Status aufhebt, mußt Du bei einem
ungeordneten Verlassen (longjmp) eben selbst dafür sorgen, daß sauber
aufgeräumt wird.
Wenn man sich schon in diese dunklen Gefilde hinabbegeben hat: sowas
kann m.E. ein guter Grund für ein goto sein. Springt man mit goto aus
einem Scope (der in deinem Fall die Variable mit dem cleanup Attribut
enthalten könnte), wird der Stack noch abgeräumt. Das Sprungziel des
gotos könnte dann dein longjmp() sein.
Allein, mir schaudert ob der Vorstellung ...
Markus F. schrieb:> Auch C++-Exceptions und/oder Destruktoren können das nur verstecken,> nicht beheben.>> Wenn Du einen Mechanismus hast, der lediglich bei einem (geordneten)> Verlassen einer Routine einen globalen Status aufhebt, mußt Du bei einem> ungeordneten Verlassen (longjmp) eben selbst dafür sorgen, daß sauber> aufgeräumt wird.
Die Idee war ja, Exceptions statt longjmp zu nutzen. Die Grundidee ist
artverwandt, aber der Compiler kann dies geordnet umsetzen und eben
Destruktoren aufrufen. Nur leider ist mindestens die Implementation vom
GCC+newlib nicht wirklich uC-tauglich. Es wäre interessant da etwas
geeignetes zu implementieren, das wäre aber ein beträchtlicher Aufwand.
Realistisch wäre es, longjmp wegzulassen, und mit Rückgabewerten
(Fehlercodes) und Destruktoren zu arbeiten. Das C++ Attribut "nodiscard"
oder die GCC Erweiterung __attribute__((warn_unused_result)) können da
helfen Fehler zu vermeiden.
Der Programmfluss ist hier ähnlich wie longjmp und ziemlich nah an dem,
was ursprünglich gewünscht war, aber eben mit automatischem Aufräumen.
Aber es ist standardisiert und braucht keine Compiler-spezifischen
Attribute. Nur leider ist eben der Overhead dafür riesig. Daher würde
ich die Fehlerbehandlung manuell machen:
Mittels "nodiscard" sorgt man dafür, dass man die Auswertung der
Rückgabe (Fehler-) Codes nicht vergisst. Die Interrupts werden immer
automatisch ge-und entsperrt. Der Programmlauf ist anders als bei
longjmp, es ist mehr Tipparbeit, aber dafür sauberer, lesbarer und auch
standardisiert.
Markus F. schrieb:> Das müsste (ich hab's nicht ausprobiert) m.E. wie gewünscht> funktionieren. Ist nicht schön, aber das ist longjmp() nie...
Das kann man so aber schlecht machen, wenn das longjmp nicht noch in
irgend einer Unterfunktion ist, und sonst braucht man das nicht
wirklich. Würde ich sowas machen, würde ich das so irgendwie machen:
1
// utils.h
2
#ifndef UTILS_H
3
#define UTILS_H
4
5
#include<setjmp.h>
6
#include<string.h>
7
8
#ifndef UNPACK
9
#define UNPACK(A) A
10
#endif
11
12
#define AUTOCLEANUP_FUNC(N,X) \
13
typedef struct { \
14
jmp_buf ac_jmp; \
15
UNPACK X \
16
} ac_ ## N ##_t; \
17
static inline void ac_ ## N ## _func_sub(ac_ ## N ##_t* ac); \