www.mikrocontroller.net

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


Autor: Erik (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallöchen,

Ich habe in C z.B. 2 Funktionen mit gleichen Parametertypen
unsigned char func1(unsigned int x, unsigned char y);
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):
void check(*func) {
    unsigned char fehler = ..func.. // Hier "func" inkl. Parameter ausführen!
    if(fehler) ..tu dies..
    else ..tu das..
}
Das heißt, ich will die Funktion "check" später z.B. in der Art aufrufen 
können:
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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Erik wrote:
> Hallöchen,
>
> Ich habe in C z.B. 2 Funktionen mit gleichen Parametertypen
>
unsigned char func1(unsigned int x, unsigned char y);
> 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):
>
void check(*func) {
>     unsigned char fehler = ..func.. // Hier "func" inkl. Parameter
> ausführen!
>     if(fehler) ..tu dies..
>     else ..tu das..
> }
> Das heißt, ich will die Funktion "check" später z.B. in der Art aufrufen
> können:
>
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.


Autor: Erik (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: A.K. (Gast)
Datum:

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

Autor: Erik (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: A.K. (Gast)
Datum:

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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.



Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Unbekannter (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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....

Autor: Erik (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ohje,

ich sehe schon, dass es wohl besser ist in jeder Funktion die "func1", 
"func2",... benutzt sowas zu machen (anstatt "check"):
unsigned char fehler = func1(08,15);
if(fehler) fehler = func1(08,15); // Nochmal versuchen
if(fehler) panic(); // Kapitulation
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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Erik wrote:

>
unsigned char fehler = func1(08,15);
> if(fehler) fehler = func1(08,15); // Nochmal versuchen
> if(fehler) panic(); // Kapitulation
> 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

Autor: Erik (Gast)
Datum:

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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.