mikrocontroller.net

Forum: Compiler & IDEs Generischer Pointer auf Funktionen?


Autor: Thomas Finke (thomas-hn) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ist es in C möglich auch generische (also untypisierte) Pointer auf 
Funktionen zu verwenden.

Ich möchte als Teil eines Structs einen Pointer auf eine Funktion 
verwenden. Später soll dieser Pointer dann allerdings für verschiedene 
Funktionstypen verwendet werden, also beispielsweise

void Funktion_1(void);
void Funktion_2(unsigned char x);
int Funktion_3(int b);

Ist sowas in C möglich? Wenn ja, wie?

Danke schonmal,

Thomas

Autor: Martin L. (melvin_the_moose)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Thomas,

ich würde sagen: nein.
Denn zur Übersetzungszeit weiß der Compiler noch nicht, auf welche 
Funktion der Zeiger später zur Laufzeit zeigen wird. Und damit weiß er 
noch nicht, ob die "gerufene" Funktion Argumente erwartet oder nicht und 
kann daher die Argumentübergabe nicht korrekt erzeugen.

Gruß

Martin

Autor: Bernhard R. (barnyhh)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
In der struct: void * ptr
Beim Aufruf: casten

Was meint der Compiler dazu?

Bernhard

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gecastete Pointer sind die Geheimwaffe, um so ziemlich alles und jedes 
in C anstellen zu können.

z.B.
int test(int);

int test(int a)
{
  return a;
}


int main(void)
{
  void (*p)(void)   // Ein Funktionspointer
  int (*q)(int);   // Ein anderer Funktionspointer
  
  p = (void (*)(void))test;   // hingecastet
  q = (int (*)(int))p;    // und hergecastet
  
  int w = q(3);

return 0;
}

Oliver

Autor: Bernhard R. (barnyhh)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es fehlt noch die Initialisierung:

Es sei innerhalb der Struktur strukt:

   void * ptr;

Im Code heißt es dann z.B.:

   int fkt(int a, void * b)
   {
...
      return 123;
   }

...
   strukt.ptr = fkt;

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vorsicht: Funktionszeiger und Objektzeiger sind vom Standard her
grundsätzlich erstmal nicht miteinander kompatibel!

Es ist also nicht OK, einen void * in einen Funktionszeiger per
typecast zu wandeln, und bspw. ein IAR-Compiler lehnt das Ansinnen
rundheraus ab.

Der Standard deckt aber den cast eines Funktionszeigers in einen
anderen, also ein void (*)(void) kann als generischer Funktionszeiger
herhalten.

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mit type casts kann sich vor allem grandios den Boden unter den Füssen 
wegziehen.

Autor: Arc Net (arc)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Da beim späteren Aufruf der unterschiedlichen Funktionen sowieso 
unterschieden werden muss welche Variante es sein soll, könnte man z.B. 
alle möglichen Funktionszeiger definieren und dann in eine Union packen.
typedef void (*IntArgFuncPtr)(int);
typedef void (*NoArgFuncPtr)(void);
typedef union {
    IntArgFuncPtr IntArgPtr;
    NoArgFuncPtr NoArgPTr;
} GenFuncPtr;

typedef struct {
   ...
   GenFuncPtr GenFunc;
} MyStruct;

Noch allgemeiner ginge es nur mit Funktionen, die eine variable 
Argumentliste erwarten.
typedef union {
    char c;
    int8_t i8;
    ...
} GenResult;

void GenFunction(GenResult* grP, ...) {
    
}

typedef void (*GenArgFuncPtr)(GenResult*, ...);

Autor: A. F. (artur-f) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>> Gecastete Pointer sind die Geheimwaffe, um so ziemlich alles und jedes
>> in C anstellen zu können.

Schöne Sache, kann aber zum Verhängnis werden, wenn man das Funktionen 
anwendet. Ich rate dir davon ab, es sei denn du bist dir 101% sicher 
dass dabei nichts schief läuft.

Kann dir jetzt aber auch keine Alternative empfehlen :(

Autor: Timmo H. (masterfx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich bin jetzt zwar nicht so fit in sachen Funktionspointer, aber ich 
habe ähnliches gemacht bei meiner Debug-Schnittstelle. In einem Struct 
sind Funtkionsname+kurzbeschreibung, Pointer auf die Funktion und die 
Syntax abgelegt. Die Funktionen arbeiten alle wie main, also mit argc 
und argv. Von daher ist es auch damit möglich eine unterschiedliche 
Anzahl von Parametern pro funktion zu übergeben. Der Prototyp bleibt 
dabei also immer der selbe. Auf jeden fall ist man dann sicher, dass 
nichts schiefgeht. Das ganze sieht in etwa so aus:
in der header-Datei
/* Prototypen für die Kommandos */
void cmd_add(char argc, char **argv);
void cmd_mul(char argc, char **argv);
void cmd_help(char argc, char **argv);
void cmd_set(char argc, char **argv);
void cmd_exit(char argc, char **argv);

void debug();
void parse_and_call_cmd(char *cmdptr);
char is_dbg_command(char *cmdptr);

typedef void generic_f (char argc, char **argv); 

typedef struct{
    char  *nptr;
    generic_f  *fptr;
    char  *syntax;
}debug_commands;
Die entsprechende C-Datei sieht dann so aus:
//#define DYN_ARGS
#include <stdio.h>
#include <stdlib.h>    /* Atoi */
#include <conio.h>    /* getch => kein Echo */
#include "debug.h"

#define MAX_ARGS  10    /* Anzahl der Argumente ohne realloc */

/* Kommando-Struktur, einfach nach Belieben erweitern
 *
 * {"BEFEHL ... Kurzbeschreibung\n", funktionsname, "Syntaxhilfe\n"},
 *         ^Leerzeichen zwischen BEFEHL und "..." zwingend notwendig!!!
 * Prototypen der Funktionen in debug.h eintragen!
 */
const debug_commands f[] = {
  {"add ... add two numbers\n", cmd_add, "add <number> <number>\n"},
  {"mul ... multiply two numbers\n", cmd_mul, "mul <number> <number>\n"},
  {"help .. Command overview/help\n", cmd_help,"help [command]\n"},
  {"?  .... Command overview/help\n", cmd_help,"? [command]\n"},
  {"exit .. exit\n", cmd_exit, "x\n"}
};

#define COMMANDS 5      /* Anzahl der Kommandos in debug_commands f[] */
#define COMMAND_BUFFER 80  /* Eingabepuffer */

int exit_debug = 0;      /* Wird durch cmd_exit auf 1 gesetzt */

/* Hauptfunktion, wird nicht verlassen 
 *
 */
void debug(){
  char       debug[COMMAND_BUFFER];  /* Buffer für Debug-Kommando */
  unsigned char   i;
  
    printf("\n========= Console ==========\n");
    printf("Enter '?' for command overview\n");
  while(1){
    i = 0;
    
    printf("\n# ");    /* Weils toll aussieht */
           
    do{
      debug[i] = getch();
      putchar(debug[i]);      /* Echo */
      if(debug[i] == '\b' && i){  /* Backspace? */
        printf(" \b");      /* Leerzeichen ausgeben + Cursor zurück*/
        i--;          /* letztes Zeichen löschen */
      }
      else{
        i++;  
      }
      if(i >= COMMAND_BUFFER -1){  /* Kommando zu lang */
        i = 0;
        printf("\nCommand too long\n");
        continue;  
      }
    /* Schleife bei ENTER verlassen */  
    }while(debug[i-1] != '\r' && debug[i-1] != '\n');
    
    if(i == 1)      /* Kein Kommando eingegeben */
      continue;
    debug[i-1] = '\0';  /* Stringterminierung */
    putchar('\n');
    
    if(i > 0)
      parse_and_call_cmd(debug);  /* Kommando suchen und ausführen */
    if(exit_debug)            /* Wenn exit eingegeben */
      break;
  }  
}

/* Addiert zwei Zahlen */
void cmd_add(char argc, char **argv){
  int no1, no2;
  if(argc == 3){
    no1 = atoi(argv[1]);
    no2 = atoi(argv[2]);
    printf("%d + %d = %d", no1, no2, no1 + no2);
  }
}

/* Multipliziert zwei Zahlen */
void cmd_mul(char argc, char **argv){
  int no1, no2;
  if(argc == 3){
    no1 = atoi(argv[1]);
    no2 = atoi(argv[2]);
    printf("%d * %d = %d", no1, no2, no1 * no2);
  }
  else
    cmd_help(2,argv-1);    /* Kleiner Trick für Syntax-Ausgabe */
}

void cmd_exit(char argc, char **argv){
  exit_debug = 1;
}

void cmd_help(char argc, char **argv){
  unsigned char  i;
  unsigned char cmd_pos;

  if(argc == 2){            /* Kommandospez. Hilfe? */
    cmd_pos = is_dbg_command(argv[1]);  
    /* Debug-Kommando? Ja => Syntax ausgeben */
    if(cmd_pos != COMMANDS + 1 ){  
      printf("\n[..] - optional argument\n<..> - required argument\n");
      printf("\n%sSyntax: %s", f[cmd_pos].nptr, f[cmd_pos].syntax);
    }
    else  /* Kommando nicht gefunden */
      printf("Command not found\n");  
  }
  else{    /* Befehlsübersicht ausgeben */ 
    for(i = 0; i < COMMANDS; i++)
      printf(f[i].nptr);
  }
}

/* Extrahiert und zählt die Argumente, sucht das Kommando über
 * is_dbg_command und führt es dann aus.
 *
 */
void parse_and_call_cmd(char *cmdptr){
  unsigned char argc=0, cmd_pos;
  
#ifndef DYN_ARGS
  unsigned char *argv[MAX_ARGS];
#else
  unsigned char **argv = NULL;
  argv = (unsigned char **)malloc(sizeof(char **));
#endif
  
  argv[argc++] = cmdptr;  /* Kommando selbst in argv[0] */
  while(1){
    cmdptr++;
    if(*cmdptr == ' '){
      *cmdptr = '\0';  
      while(*++cmdptr == ' ');  /* Leerzeichen überspringen */
#ifdef DYN_ARGS
      argv = realloc(argv, (argc+1)*sizeof(char **));
#else
      if(argc > 9){
        printf("\nToo many arguments!\n");
        return;  
      }        
#endif
      argv[argc++] = cmdptr;
    }
    else if(*cmdptr == '\0'){
      break;
    }
  }
  cmd_pos = is_dbg_command(argv[0]);
  if(cmd_pos == COMMANDS + 1)          /* Befehl nicht gefunden */
    printf("\nUnknown command\n");
  else{
    (f[cmd_pos].fptr)(argc, (char **)argv); /*  Funktion aufrufen */
  }
#ifdef DYN_ARGS
  free(argv);
#endif
}

/* Sucht das Kommando in dem debug_commands-Struct und liefert
 * die Position im Struct zurück
 */
char is_dbg_command(char *cmdptr){
  unsigned char i=0, j=0, error;
  
  while(i < COMMANDS ){  /* Alle Kommandos durchsuchen */
    error = 0;
    while(f[i].nptr[j] != ' '){  
      if(f[i].nptr[j] != cmdptr[j]){
        error = 1;
        break;    /* Abweichung => nächsten Befehl testen */
      }
      j++;    
    }
      if(!(error == 0 && (cmdptr[j] == ' ' || cmdptr[j] == '\0') )){
      error = 1;
      i++;
      j=0;
    }  
    else        /* Kommando gefunden, error = 0 */
      break;
  }
  if(error==0)
    return i;
  else
    return COMMANDS+1;
}

/* Main, nur zum testen */
int main(){
    debug();
    return 0;
}
Ich glaube es ist relativ fehlerfrei und ist bei mir ständig im einsatz 
(hauptsächlich auf meinem AVR).

Autor: I_ H. (i_h)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn der Compiler es nicht macht, kann man's immernoch über ein union 
implementieren. Auch wenn's nicht besonders schön ist, es geht.

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.