Forum: PC-Programmierung Zeiger auf Funktionen mal wieder planlos :-(


von Christian J. (Gast)


Lesenswert?

Hallo,

ich habe jetzt genug ausprobiert, weiss nicht mehr weiter.

Funktion 1:
FlagStatus I2C_GetFlagStatus(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG)
Aufruf:
I2C_GetFlagStatus(I2C_KANAL, I2C_EVENT_MASTER_MODE_SELECT)

Funktion 2:
ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
Aufruf:
I2C_CheckEvent(I2C_KANAL, I2C_EVENT_MASTER_MODE_SELECT);

Beide Funktionen möchte ich in eine Timeout Routine packen, da beide 
Funktionen in while Schleifen verwendet werden, bis das Ereignis 
eintrifft.
Die beide Rückgabetypen sind 32 Bit Integers, einfache enums. Der Typdef 
ist ein uint16_t.

Wie formuliere ich nun eine Timeout Funktion, der ich beide 
gleichartigen Funktionen als Parameter übergeben kann? Und wie würde 
dann der Aufruf lauten?

timeout(.........)

1. Versuch:

uint8_t timed ( uint32_t (*f) (uint16_t*,uint32_t) ) {

    .......
    return 1;
}

Aber bei den Aufrufen kommen dann nur noch Compilerfehler, egal wie ich 
es drehe.

src\eeprom.c|460|error: incompatible type for argument 1 of 'timed'|
src\eeprom.c|439|note: expected 'uint32_t (*)(uint16_t *, uint32_t) {aka 
long unsigned int (*)(short unsigned int *, long unsigned int)}' but 
argument is of type 'ErrorStatus {aka enum <anonymous>}'|
||=== Build finished: 1 errors, 0 warnings (0 minutes, 1 seconds) ===|


Kriege das nicht hin :-(

Gruss,
Christian

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Christian J. schrieb:

> ich habe jetzt genug ausprobiert, weiss nicht mehr weiter.

Experimentelle Informatik? ;-)


> 1. Versuch:
>
> uint8_t timed ( uint32_t (*f) (uint16_t*,uint32_t) ) {

2. Versuch:
1
uint8_t timed ( uint32_t (*f1)(I2C_TypeDef*,uint32_t), uint32_t (*f1)(I2C_TypeDef*,uint32_t) ) {
2
...

von Christian J. (Gast)


Lesenswert?

ja, zeiger sind meine Krise, mal das * vor einem Ausdruck, dann mal 
dahinter usw...

Das doppelte f1 wird erstmal angemeckert. Setze ich es f2 geht es durch 
aber wie sieht dann der Aufruf aus? Ganz verstehe ich den Ausdruck auch 
nicht.
Es soll ja nur der passende Stackframe zusammen gebaut werden mit zwei 
Zahlen als Parametern. Ok, ein Zahl wird als Zeiger übergeben.

timed (I2C_CheckEvent(I2C_KANAL, I2C_EVENT_MASTER_MODE_SELECT));

ergibt

src\eeprom.c|460|error: incompatible type for argument 1 of 'timed'|
src\eeprom.c|439|note: expected 'ErrorStatus (*)(I2C_TypeDef *, 
uint32_t) {aka enum <anonymous> (*)(struct <anonymous> *, long unsigned 
int)}' but argument is of type 'ErrorStatus {aka enum <anonymous>}'|
src\eeprom.c|460|error: too few arguments to function 'timed'|
src\eeprom.c|439|note: declared here|
||=== Build finished: 2 errors, 0 warnings (0 minutes, 1 seconds) ===|

Beitrag #5085496 wurde von einem Moderator gelöscht.
von Christian J. (Gast)


Lesenswert?

So, das kompiliert er problemlos durch:

   ErrorStatus (*ptr1) (I2C_TypeDef*, uint32_t);
   FlagStatus (*ptr2) (I2C_TypeDef*, uint32_t);

   ErrorStatus res1;
   FlagStatus res2;

   ptr1 = I2C_CheckEvent;
   res1 = (*ptr1)(I2C_KANAL, I2C_EVENT_MASTER_MODE_SELECT);

   ptr2 = I2C_GetFlagStatus;
   res2 = (*ptr2)(I2C_KANAL, I2C_FLAG_STOPF);


und das läuft auch:

   uint32_t res1,res2;

   ptr1 = I2C_CheckEvent;
   res1 = (uint32_t)(*ptr1)(I2C_KANAL, I2C_EVENT_MASTER_MODE_SELECT);

   ptr2 = I2C_GetFlagStatus;
   res2 = (uint32_t)(*ptr2)(I2C_KANAL, I2C_FLAG_STOPF);


Da ErrorStatus und Flagstatus aber beides nur uint32 sind, wenn man sich 
in der Lib durchhangelt fehlt mir nur noch das Rezept wie ich da einen 
Cast einbringen, so dass ich ptr1 und pt2 zusammen fassen kann.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Christian J. schrieb:
> Wie formuliere ich nun eine Timeout Funktion, der ich beide
> gleichartigen Funktionen als Parameter übergeben kann? Und wie würde
> dann der Aufruf lauten?

Hier ist ein typedef tatsächlich hilfreich.
1
typedef uint32_t (*t_func) (uint16_t*,uint32_t);

Und damit schreibt sich Deine Funktion fast wie von selbst:
1
FlagStatus I2C_GetFlagStatus(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG);
2
ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT);
3
4
5
6
uint8_t timed(t_func fptr1, t_func fptr2)
7
{
8
  uint32_t bla;
9
  uint32_t fusel;
10
  I2C_TypeDef I2CData;
11
12
13
14
15
  if (whatever)
16
    fusel = fptr1(&I2CData, bla);
17
  else
18
    fusel = fptr2(&I2CData, bla);
19
20
  return 1;
21
}
22
23
...
24
25
uint8_t result;
26
27
result = timed(I2C_GetFlagStatus, I2C_CheckEvent);


Allerdings stimmen Deine Funktionssignaturen der beiden Funktionen nicht 
überein, die haben nicht den gleichen Rückgabewert.

Der Funktionspointertyp ist mit uint32_t definiert, "FlagStatus" und 
"ErrorStatus" aber heißen jeweils anders.

von Christian J. (Gast)


Lesenswert?

Moment...

uint8_t timed(t_func fptr1, t_func fptr2)

Du willst da beide Funktionen übergeben aber es wird jeweils nur die 
eine oder die andere übergeben.

Diese beiden Aufrufe

FlagStatus I2C_GetFlagStatus(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG);
ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT);

kommen abwechselnd sehr, sehr häufig vor im Programm, natürlich mit den 
entsprechenden Parametern und sie werden jedesmal in eine Timeout 
Schleife gekapselt. Aktuell habe ich so rund 20 einzelne kapselungen. 
Und das will ich einfacher machen.

FlagStatus und ErrorStatus sind aus den StdPeriph Libs für den ARM. 
Beides sind sehr verschwurbelte Integers, einer ein Struct, der andere 
ein enum.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Christian J. schrieb:
> Du willst da beide Funktionen übergeben aber es wird jeweils nur die
> eine oder die andere übergeben.

Dann hättest Du die Aufgabenstellung präzisieren sollen, ich bin nicht 
der erste, der drauf reingefallen ist.

Das Prinzip aber sollte erkennbar sein, und wenn Du jetzt noch dafür 
sorgst, daß Deine Funktionssignaturen gleich sind, brauchst Du auch 
keine Typecasts.

Sonst musst Du halt sowas unschönes schreiben:
1
result = timed((t_func) I2C_GetFlagStatus);
2
result = timed((t_func) I2C_CheckEvent);


Unschön ist so etwas, weil es potentielle Fehler verdeckt.

von Christian J. (Gast)


Lesenswert?

Ok, ich probiers mal, Prinzip ja soweit verstanden.... und wenn es gar 
nicht hinhaut kapsel ich halt beide Funktionen in MyFunc1, MyFunc2 und 
bringe die Schleife dort ein. Wollte halt nur mal verstehen wie das 
funktioniert mit der Übergabe von Funktionen.

von Christian J. (Gast)


Lesenswert?

Wie spricht man denn die Parameter I2C_KANAL, 
I2C_EVENT_MASTER_MODE_SELECT an im Funktionsrumpf?

typedef ErrorStatus (*t_func) (I2C_TypeDef*,uint32_t);

uint8_t timed(t_func fptr) {

    while ((*fptr) != 0) {
       timeout--;
       if (timeout .....
       ....
    }
    return 1;
}

Ich muss fptr ja auch aufrufen.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Christian J. schrieb:
> while ((*fptr) != 0) {

Was soll das bezwecken?

von Felix U. (ubfx)


Lesenswert?

Christian J. schrieb:
> Wie spricht man denn die Parameter I2C_KANAL,
> I2C_EVENT_MASTER_MODE_SELECT an im Funktionsrumpf?

Ein Funktionspointer ist genau das, ein Pointer auf die Funktion. Dass 
du bei der Deklaration des Pointers die ParamterTYPEN (nicht Namen) mit 
angibst, dient dem Compiler nur dazu, die Parameter korrekt übergeben zu 
können. Die Parameter musst du gesondert übergeben.
1
typedef ErrorStatus (*t_func) (I2C_TypeDef*,uint32_t);
2
3
uint8_t timed(t_func fptr, I2C_TypeDef *param1, uint32_t param2) {
4
5
    fptr(param1, param2);
6
}

Klingt mir übrigens eher nach einem Fall für ein Makro.

von Christian J. (Gast)


Lesenswert?

So langsam rieselt der Kalk.... aber leider sind die Funktionssignaturen 
nicht gleich, jeder Aufruf scheitert. na egal, heute nicht mehr. 
Jedenfalls Danke für die Erklärungen!

typedef uint32_t (*t_func) (I2C_TypeDef*,uint32_t);

uint8_t timed(t_func fptr, I2C_TypeDef* param1, uint32_t param2  ) {

    fptr(param1, param2);
    return 1;
}

Kompiliert durch.

timed(I2C_CheckEvent(I2C_KANAL, I2C_EVENT_MASTER_MODE_SELECT));

Wird angemeckert wegen inkompatiblen Rückgabewert.

src\eeprom.c|440|note: expected 't_func {aka long unsigned int 
(*)(struct <anonymous> *, long unsigned int)}' but argument is of type 
'ErrorStatus {aka enum <anonymous>}'|

von Felix U. (ubfx)


Lesenswert?

Christian J. schrieb:
> timed(I2C_CheckEvent(I2C_KANAL, I2C_EVENT_MASTER_MODE_SELECT));
>
> Wird angemeckert wegen inkompatiblen Rückgabewert.
Jetzt aber vor lauter Pointern die Basics nicht vergessen. die 
timed-Funktion hat 3 Parameter. Und du übergibst einen, nämlich den 
Rückgabewert von I2C_CheckEvent.


Was du machen willst ist:
1
timed(&I2C_CheckEvent, I2C_KANAL, I2C_EVENT_MASTER_MODE_SELECT);

Das & ist nicht notwendig, verdeutlicht aber, dass hier nicht die 
Funktion gecallt (so wie du es gemacht hast) sondern referenziert wird.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Felix U. schrieb:
> Christian J. schrieb:
>> timed(I2C_CheckEvent(I2C_KANAL, I2C_EVENT_MASTER_MODE_SELECT));
>>
>> Wird angemeckert wegen inkompatiblen Rückgabewert.
> Jetzt aber vor lauter Pointern die Basics nicht vergessen. die
> timed-Funktion hat 3 Parameter. Und du übergibst einen, nämlich den
> Rückgabewert von I2C_CheckEvent.
>
>
> Was du machen willst ist:
>
1
> timed(&I2C_CheckEvent, I2C_KANAL, I2C_EVENT_MASTER_MODE_SELECT);
2
>
>
> Das & ist nicht notwendig, verdeutlicht aber, dass hier nicht die
> Funktion gecallt (so wie du es gemacht hast) sondern referenziert wird.

Das hier I2C_CheckEvent nicht aufgerufen wird, sieht man aber daran, das 
hinter dem Namen I2C_CheckEvent der call-operator "()" nicht steht.

Was hier passiert ist etwas anderes: I2C_CheckEvent ist der Bezeichner 
einer Funktion (function-designator). Für Funktionsbezeichner gibt es 
die function-to-pointer Konvertierung: also I2C_CheckEvent evaluiert 
(fast überall und immer, wenn als rvalue gebraucht) zu einem Zeigerwert 
mit dem Typ "Zeiger auf Funktion ...".  Nur wenn der Adress-Operator "&" 
auf den Bezeichner einer Funktion wie I2C_CheckEvent angewendet wird, 
wird der Bezeichnet nicht implizit konvertiert, und es passiert das 
Erwartete: es wird die Adresse, also ein Zeigerwert mit dem Type "Zeiger 
auf Funktion ..." bestimmt.

Umgekehrt geht das auch: wird der call-Operator "()" auf den Bezeichner 
einer Funktion angewendet wie bei I2C_CheckEvent(), so wird die Funktion 
aufgerufen. Wird der call-Operator auf einen Zeiger auf eine Funktion 
angewendet, so wird auch die Funktion aufgerufen:
1
void f() {}
2
int main() {
3
   f(); // f wird aufgerufen
4
   void (*p)();
5
   p = f; // f wird zu einem Zeiger-auf-Funktion implizit konvertiert
6
   p(); // call-Op angewendet auf Zeiger auf Funktion
7
}

Man kann auch folgendes machen:
1
   (******p)();

Hier ist dann `*p` das Funktionsobjekt, das wird implizit zu einem 
Zeiger-auf-Funktion konvertiert, so dass `**p` wieder das 
Funktionsobjekt ist, u.s.w.

Natürlich ist &p etwas anderes, denn p ist eine lvalue und kein 
Funktionsbezeichner ...

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.