Forum: Mikrocontroller und Digitale Elektronik Übergabe einer Funktion an eine Funktion???


von Erik (Gast)


Lesenswert?

Hallöchen,

Ich habe in C z.B. 2 Funktionen mit gleichen Parametertypen
1
unsigned char func1(unsigned int x, unsigned char y);
2
unsigned char func2(unsigned int x, unsigned char y);

Jetzt müsste ich zwecks Fehlerbehandlung diese Funktionen durch eine 
andere kontrollieren lassen, also was in der Art (Pseudocode):
1
void check(*func) {
2
    unsigned char fehler = ..func.. // Hier "func" inkl. Parameter ausführen!
3
    if(fehler) ..tu dies..
4
    else ..tu das..
5
}
Das heißt, ich will die Funktion "check" später z.B. in der Art aufrufen 
können:
1
check(func1(08, 15))
...oder so ähnlich.
Klar könnte ich einen Funktionspointer nehmen, müsste dann aber "check" 
alle Parameter von "funcX" übergeben.
Gibt es in C vielleicht eine elegantere Lösung?

Vielen Dank,
Erik

von Karl H. (kbuchegg)


Lesenswert?

Erik wrote:
> Hallöchen,
>
> Ich habe in C z.B. 2 Funktionen mit gleichen Parametertypen
>
1
unsigned char func1(unsigned int x, unsigned char y);
2
> unsigned char func2(unsigned int x, unsigned char y);
>
> Jetzt müsste ich zwecks Fehlerbehandlung diese Funktionen durch eine
> andere kontrollieren lassen, also was in der Art (Pseudocode):
>
1
void check(*func) {
2
>     unsigned char fehler = ..func.. // Hier "func" inkl. Parameter
3
> ausführen!
4
>     if(fehler) ..tu dies..
5
>     else ..tu das..
6
> }
> Das heißt, ich will die Funktion "check" später z.B. in der Art aufrufen
> können:
>
1
check(func1(08, 15))
> ...oder so ähnlich.
> Klar könnte ich einen Funktionspointer nehmen, müsste dann aber "check"
> alle Parameter von "funcX" übergeben.

Irgendwie müssen die Argumente aber bis zum Aufruf der
eigentlichen Funktionen durchkommen.
* Entweder du gibst die Argumente geimeinsam mit dem Funktionspointer
  an check
* check holt sich die Argumente irgendwo her (globale Variablen oder
  vielleicht kommen die ja auch aus einer Berechnung)
* Du änderst die auzurufenden Funktionen so ab, dass sie keine
  Argumente mehr haben wollen


Wenn es dir nur darum geht, dass es ein paar Funktionen
gibt, die einen Fehlercode zurückliefern und du deine
Funktion machen möchtest, die den Fehlercode auswertet,
was spricht dann dagegen, das genau so zu implementieren:

void check( unsigned char FehlerCode )
{
  if( FehlerCode == irgendwas )
    mach was
  else
    mach was anderes
}


und das dann so zu benutzen

  check( func1( 08, 15 ) );

die Funktion func1 wird mit ihren Argumenten 08 und 15 aufgerufen
und liefert als Ergebnis einen Fehlercode, dieser retournierte
Fehlercode wird an die Funktion check weitergereicht, die ihn
dann auswertet.


von Erik (Gast)


Lesenswert?

@Karl heinz Buchegger
Zunächst mal Danke!
>void check( unsigned char FehlerCode )
Das man das machen kann ist klar. Je nach Schweregrad des Fehlers soll 
"check" selbst aber versuchen evtl. "funcX" nochmals aufzurufen. Wenns 
dann immer nach nicht geklappt hat, wird kapituliert oder so...
"check" einen Funktionspointer und dann nochmals sämtliche Parameter der 
"gepointerten" Funktion zu übergeben wollte ich vermeinden. Ich werde es 
dann aber eben so machen, wenn es nicht anders geht.

Ich dachte nur, es ginge vielleicht, einen Funktionspointer INKL. 
Parameter zu erstellen und aufzurufen, da ja "eigentlich nur" die Daten 
auf dem Stack abgearbeitet werden müssen...

Gruß, Erik

von A.K. (Gast)


Lesenswert?

In C++ ist so etwas möglich und auch durchaus üblich, in C wird es 
umständlich.

von Erik (Gast)


Lesenswert?

> "check" einen Funktionspointer und dann nochmals sämtliche Parameter der
> "gepointerten" Funktion zu übergeben wollte ich vermeinden. Ich werde es
> dann aber eben so machen, wenn es nicht anders geht.
Was mir daran noch nicht gefällt, ist das die Parameter dann ja 2mal auf 
dem Stack landen. Einmal bei der Übergabe an "check" und nochmal beim 
Aufruf der Funktion auf die der Pointer zeigt.

@A.K.
> in C wird es umständlich
Wie umständlich ist umständlich denn :-) ?

Danke,
Erik

von A.K. (Gast)


Lesenswert?

C++ "zu Fuss": Struct definieren, Parameter drin, Funktionszeiger drin. 
Und die dann übergeben.

von Karl H. (kbuchegg)


Lesenswert?

Erik wrote:
>> in C wird es umständlich
> Wie umständlich ist umständlich denn :-) ?

Ziemlich.
Eine Möglichkeit wäre es, für Funktion und Argumente eine
struct zu bauen.

struct CallFunc
{
  FuncPtr theFunction;
  uint8_t Argument1;
  uint8_t Argument2;
};

beim Aufrufer wird die Struktur ausgefüllt und so ein Objekt
an check übergeben.

In C++ geht das wunderbar, weil es dort Konstruktoren gibt,
die so ein Objekt 'on the fly' erzeugen und initialisieren
können:

struct CallFunc
{
  CallFunc( FuncPtr A, uint8_t B, uint8_t C ) :
          theFunction( A ), Argument1( B ), Argument2( C )
           {}

  FuncPtr theFunction;
  uint8_t Argument1;
  uint8_t Argument2;
};

Damit ist dann möglich:

  check( CallFunc( Function1, 08, 15 ) );

In C gibt es das aber nicht, und dann wirds unhandlich:

  struct CallFunc Arg;
  Arg.theFunction = Function1;
  Arg.Argument1 = 08;
  Arg.Argument2 = 15;

  check( &Arg );

Ah. Man könnte sich mit einer Hilfsfunktion behelfen:

struct CallFunc* CreateObj( FuncPtr A, uint8_t B, uint8_t C )
{
  struct CallFunc* pNew = malloc( sizeof struct CallFunc );
  pNew->theFunction = A;
  pNew->Argument1 = B;
  pNew->Argument2 = C;
  return pNew;
}

  check( CreateObj( Function1, 08, 15 ) );

darf dann aber nicht vergessen in check die allokierte
Struktur wieder freizugeben.

Macht man keine dynamische Allokierung

struct CallFunc CreateObj( FuncPtr A, uint8_t B, uint8_t C )
{
  struct CallFunc New;
  New.theFunction = A;
  New.Argument1 = B;
  New.Argument2 = C;
  return pNew;
}

dann wird wieder eine Menge über den Stack rumkopiert.

Hier fehlen dann in C die Möglichkeiten der Optimierungen die
C++ über Referenzen ermöglichen, das alles wieder zu streamlinen.



von A.K. (Gast)


Lesenswert?

Helfen könnte, das zumindest GCC lokale structs initialisieren kann, 
auch wenn sie nicht static sind. Ob das auch in C99 drin ist weiss ich 
nicht.

von Unbekannter (Gast)


Lesenswert?

Du kannst auch Makros verwenden. Das ist aber u.U. nicht sehr eingängig. 
Ungefähr so:


char check_impl( char fehler )
{
  if ( fehler )
  {
     if ( soll_wiederholt_werden )
       return 1;
     else
       mach_was_anderes();
  }
  return 0; /* Nicht wiederholen */
}

#define check( FCALL )    \
  do { char f; do {f=FCALL;} while(check_impl(f)); } while(0)


Das funktioniert, ist aber nicht sehr hübsch....

von Erik (Gast)


Lesenswert?

Ohje,

ich sehe schon, dass es wohl besser ist in jeder Funktion die "func1", 
"func2",... benutzt sowas zu machen (anstatt "check"):
1
unsigned char fehler = func1(08,15);
2
if(fehler) fehler = func1(08,15); // Nochmal versuchen
3
if(fehler) panic(); // Kapitulation
4
else ok(); // ALLES KLAR!
Ist dann eben etwas Copy&Paste, schont aber den Stack, naja...
Ich denke aber, alles andere wäre für diesen Fall zu viel des Guten.

Vielen lieben Dank euch allen trotzdem für die tollen Antworten.
Ich werde das auf jeden Fall in "schlimmeren Fällen" (diese kommen 
bestimmt) in Erwägung ziehen.

Beste Grüße,
Erik

von Karl H. (kbuchegg)


Lesenswert?

Erik wrote:

>
1
unsigned char fehler = func1(08,15);
2
> if(fehler) fehler = func1(08,15); // Nochmal versuchen
3
> if(fehler) panic(); // Kapitulation
4
> else ok(); // ALLES KLAR!
> Ist dann eben etwas Copy&Paste,

Wenn diese Sequenz immer gleich ist, dann würde sich
in der Tat ein Makro dafür anbieten. Dann macht jemand
anderer den Copy&Paste

von Erik (Gast)


Lesenswert?

@Karl heinz Buchegger
Stimmt!
Ausser
1
func1(08,15)
ändert sich normalerweise nichts.
Klar,Danke!
Wei war das mit dem Wald und den Bäumen :-)

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.