Hallo zusammen,
ich rätselt seit gestern an einer Idee herum. Und zwar brauche ich für
eine recht vielseitige Anwendung die Möglichkeit einen Funktionszeiger
im Fehlerfall auf einen Fehlerhandler zu verbiegen. Dabei soll im Falle
mehrerer verschiedener Funktionszeiger mit unterschiedlicher Signatur
immer selbger Fehlerhandler angesprungen werden. Da ich nicht weiss, ob
folgender Code sicher ist (funktionieren und compilieren tut er ohne
Probleme), möchte ich dies einmal Euch Fachleuten vorstellen. Ich hoffe
mir kann jemand kurz erklären wieso es geht oder wieso es eben nicht
sicher ist. Hier einmal ein einfaches Beispiel dessen was ich vor habe:
typedef uint8_t(*cb_ftn)(uint8_t param_u8, uint16_t param_u16);
typedef void (*default_handler)(void);
uint8_t testfunction_1(uint8_t param_u8, uint16_t param_u16)
{
... do something usefull
}
void testfunction_error_handler(void)
{
... do something usefull
}
Im Programm dann schließlich:
cb_ftn callback = testfunction_1;
callback(1,2); //--> funktioniert logischerweise
callback = (*cb_ftn)testfunction_error_handler;
callback(1,2); //--> hier entsteht meine Frage...
Frage: Aus meiner Sicht ist der Code sicher, sofern nach dem zweiten
Aufruf von callback(1,2) die Parameter 1 und 2 vom Stack wieder entfernt
werden. Ich habe etwas Sorge, dass dies vielleicht nicht geschieht und
somit der Stack bei jedem Aufruf wächst. Mir ist auch bewusst, dass das
Casten von Funktionszeigern auf Funktionszeiger anderer Signaturen zu
unbestimmtem Verhalten führt. Aber ist dies auch so, wenn die Funktion
die dem Zeiger zugewiesen wird überhaupt gar keinen Funktionsparameter
nutzt? Im Prinzip landen die Parameter dann auf dem Stack aber werden
nie benutzt.
Für eine Auskunft wäre ich sehr dankbar. Ich zermatere mir das Hirn seit
Gestern...
Gernhart Reinholzen schrieb:> Frage: Aus meiner Sicht ist der Code sicher, sofern nach dem zweiten> Aufruf von callback(1,2) die Parameter 1 und 2 vom Stack wieder entfernt> werden.
Das macht in C der Aufrufer, daher ist das nicht das Problem. Aber ...
wie schon Frank meinte, sinnvoller dürfte es sein, die Parameter auch
dem Errorhandler mitzugeben, der muss sie ja nicht auswerten.
Hallo, danke erst einmal für die Rückmeldungen.
Der Errorhandler dient dem Abfangen von verschiedenen Fehlern,
verschiedener Funktionszeiger. Es ist nicht möglich ihm die Parameter
mitzugeben. Leider existiert ein Großteil des Codes bereits und ich
müsste dann rund 100 verschiedene Errorhandler nachziehen. Dies möchte
ich vermeiden. Was ich sicherstellen möchte ist an sich nur, dass
während des Debuggens dieser eine Handler angesprungen wird. So muss ich
nur einen Handler implementieren und den weiteren Code nur an sehr
wenigen Stellen anfassen.
So wie ich das sehe: Der Caller räumt auf --> dann dürfte das Ganze an
sich ja kein Problem sein, solange der Errorhandler die Parameter nicht
nutzt.
Gernhart Reinholzen schrieb:> Es ist nicht möglich ihm die Parameter mitzugeben.
Dann ist das Softwaredesign kaputt.
Welchen Sinn hat eigentlich der Aufruf eines "Errorhandlers", wenn der
gar nicht herausfinden kann, warum er aufgerufen wird?
Ganz genau so ist es. Das Design ist kaputt. Der Handler hat nur einen
einzigen Sinn und Zweck:
--> Beim Debugging herauszufinden wo und wieso ein bestimmter
Funktionszeiger auf einen Treiber auf einmal auf NULL steht.
Es dient lediglich der Fehlersuche.
Gernhart Reinholzen schrieb:> typedef uint8_t(*cb_ftn)(uint8_t param_u8, uint16_t param_u16);> typedef void (*default_handler)(void);>> uint8_t testfunction_1(uint8_t param_u8, uint16_t param_u16)> void testfunction_error_handler(void)>> Im Programm dann schließlich:>> cb_ftn callback = testfunction_1;> callback(1,2); //--> funktioniert logischerweise> callback = (*cb_ftn)testfunction_error_handler;> callback(1,2); //--> hier entsteht meine Frage...>> Frage: Aus meiner Sicht ist der Code sicher,
Es ist Undefined Behavour und also keinesfalls sicher.
> Ich habe etwas Sorge,
Zu recht. Verwende eine Union, die zudem speichert, welcher Prototyp
verwendet werden soll; das bedeutet dann allerdings um jeden Aufruf
if-else Klauseln, wie der Aufruf zu geschehen hat, dh. ob er aussieht
wie ein cb_ftn oder wie ein default_handler.
Andere Lösung ist, den Fehlerhandler vom gleichen Prototyp zu haben wie
cb_ftn und die übergebenen Parameter einfach nicht zu nutzen.
> Mir ist auch bewusst, dass das Casten von Funktionszeigern auf> Funktionszeiger anderer Signaturen zu unbestimmtem Verhalten führt.
Ja.
> Aber ist dies auch so, wenn die Funktion die dem Zeiger zugewiesen> wird überhaupt gar keinen Funktionsparameter nutzt?
Ja.
Gernhart Reinholzen schrieb:> --> Beim Debugging herauszufinden wo und wieso ein bestimmter> Funktionszeiger auf einen Treiber auf einmal auf NULL steht.
Fragen:
1. Auf welcher Platform / CPU arbeitest DU?
2. Welche Tools (Compiler, Debugger)?
2. Wieviele Funktionszeiger musst Du prüfen?
Wenn ich Dich richtig verstehe, musst Du Fehlersuche in vorhandenem Code
machen. Nach meiner Erfahrung sollte man zurest ohne Code-Modifikationen
arbeiten, oder sie so implementieren, dass nacher keine Leichen zurück
bleiben.
Gernhart Reinholzen schrieb:> Ganz genau so ist es. Das Design ist kaputt. Der Handler hat nur einen> einzigen Sinn und Zweck:>> --> Beim Debugging herauszufinden wo und wieso ein bestimmter> Funktionszeiger auf einen Treiber auf einmal auf NULL steht.>> Es dient lediglich der Fehlersuche.
Wenn Du nicht selbst den Zeiger auf NULL setzt, wer gibt dir dann noch
die Gewissheit, dass der RAM Inhalt überhaupt noch in Ordnung ist. Sowas
führt dann häufig zu intensiven Fehlersuchen, wo keine sind (daher so
intensiv).
Am einfachsten finde ich 'write Breakpoints' auf den Zeiger zu setzen.
Danke erst einmal für alle Antworten an dieser Stelle.
Die write Breakpoints funktionieren in diesem Projekt leider nur selten.
Keine Ahnung woran es liegt. Allerdings nehme ich mir den Rat zu Herzen
das Fehlerhandling nicht auf meine beabsichtigte Art eben schnell
reinzufummeln.
Für die Zukunft bekommen zumindest meine Module / Treiber
default-Handler die auf die entsprechende Signatur passen.
Danke für all die qualifizierten Ratschläge und die Mühen die sich
einige damit gemacht haben!
Daniel A. schrieb:> Steffen R. schrieb:>> 'write Breakpoints'>> Auch bekannt als /Watchpoints/
Beim Keil findet man diese unter 'Breakpoint' Access 'write'. Watchpoint
kenne ich nur dahingehend, das sie das Watchfenster beim drüberlaufen
auffrischen.
Spielt aber für den Sachverhalt keine Rolle. Ich denke man konnte
erkennen, dass ich im Debugger beim Schreiben auf den Pointer anhalte.
Kann man häufig auch dahingehend einschränken, dass nur beim Schreiben
von NULL angehalten wird.