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...
Warum hat der Fehlerhandler nicht einfach die gleichen Parameter als Übergabewert? Muss sie ja nicht verwenden...
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.
Einfache C-Regel: der Caller räumt auf. Wie würde sonnst printf funktionieren können.
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.
In C++ geht so etwas sicher und standardkonform mit std::function und std::bind.
1 | using cb_fun = std::function <uint8_t (uint8_t, uint16_t)>; |
2 | |
3 | cb_fun callback = testfunction_1; |
4 | callback(1,2); //--> funktioniert logischerweise |
5 | callback = std::bind (testfunction_error_handler); |
Ungetestet... Aber ich habe den Verdacht dass du deinen Code nicht eben fix portieren kannst :)
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.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.