Forum: Mikrocontroller und Digitale Elektronik Funktionszeiger Typecast


von Gernhart Reinholzen (Gast)


Lesenswert?

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...

von Frank (Gast)


Lesenswert?

Warum hat der  Fehlerhandler nicht einfach die gleichen Parameter als 
Übergabewert?
Muss sie ja nicht verwenden...

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von Carl D. (jcw2)


Lesenswert?

Einfache C-Regel: der Caller räumt auf.
Wie würde sonnst printf funktionieren können.

von Gernhart Reinholzen (Gast)


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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?

von Gernhart Reinholzen (Gast)


Lesenswert?

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.

von Daniel A. (daniel-a)


Lesenswert?

Ich kann für Errorhändling exceptions4c empfehlen:
https://github.com/guillermocalvo/exceptions4c

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

von Bear ˁ˚ᴥ˚ˀ (Gast)


Lesenswert?

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.

von Steffen R. (steffen_rose)


Lesenswert?

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.

von Dr. Sommer (Gast)


Lesenswert?

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 :)

von Daniel A. (daniel-a)


Lesenswert?

Steffen R. schrieb:
> 'write Breakpoints'

Auch bekannt als Watchpoints

von Gernhart Reinholzen (Gast)


Lesenswert?

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!

von Steffen R. (steffen_rose)


Lesenswert?

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
Noch kein Account? Hier anmelden.