Forum: Compiler & IDEs Generischer Pointer auf Funktionen?


von Thomas F. (thomas-hn) Benutzerseite


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

von Martin L. (melvin_the_moose)


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

von Bernhard R. (barnyhh)


Lesenswert?

In der struct: void * ptr
Beim Aufruf: casten

Was meint der Compiler dazu?

Bernhard

von Oliver (Gast)


Lesenswert?

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

z.B.
1
int test(int);
2
3
int test(int a)
4
{
5
  return a;
6
}
7
8
9
int main(void)
10
{
11
  void (*p)(void)   // Ein Funktionspointer
12
  int (*q)(int);   // Ein anderer Funktionspointer
13
  
14
  p = (void (*)(void))test;   // hingecastet
15
  q = (int (*)(int))p;    // und hergecastet
16
  
17
  int w = q(3);
18
19
return 0;
20
}

Oliver

von Bernhard R. (barnyhh)


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;

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

von Andreas K. (a-k)


Lesenswert?

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

von Arc N. (arc)


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.
1
typedef void (*IntArgFuncPtr)(int);
2
typedef void (*NoArgFuncPtr)(void);
3
typedef union {
4
    IntArgFuncPtr IntArgPtr;
5
    NoArgFuncPtr NoArgPTr;
6
} GenFuncPtr;
7
8
typedef struct {
9
   ...
10
   GenFuncPtr GenFunc;
11
} MyStruct;

Noch allgemeiner ginge es nur mit Funktionen, die eine variable 
Argumentliste erwarten.
1
typedef union {
2
    char c;
3
    int8_t i8;
4
    ...
5
} GenResult;
6
7
void GenFunction(GenResult* grP, ...) {
8
    
9
}
10
11
typedef void (*GenArgFuncPtr)(GenResult*, ...);

von A. F. (artur-f) Benutzerseite


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

von Timmo H. (masterfx)


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
1
/* Prototypen für die Kommandos */
2
void cmd_add(char argc, char **argv);
3
void cmd_mul(char argc, char **argv);
4
void cmd_help(char argc, char **argv);
5
void cmd_set(char argc, char **argv);
6
void cmd_exit(char argc, char **argv);
7
8
void debug();
9
void parse_and_call_cmd(char *cmdptr);
10
char is_dbg_command(char *cmdptr);
11
12
typedef void generic_f (char argc, char **argv); 
13
14
typedef struct{
15
    char  *nptr;
16
    generic_f  *fptr;
17
    char  *syntax;
18
}debug_commands;
Die entsprechende C-Datei sieht dann so aus:
1
//#define DYN_ARGS
2
#include <stdio.h>
3
#include <stdlib.h>    /* Atoi */
4
#include <conio.h>    /* getch => kein Echo */
5
#include "debug.h"
6
7
#define MAX_ARGS  10    /* Anzahl der Argumente ohne realloc */
8
9
/* Kommando-Struktur, einfach nach Belieben erweitern
10
 *
11
 * {"BEFEHL ... Kurzbeschreibung\n", funktionsname, "Syntaxhilfe\n"},
12
 *         ^Leerzeichen zwischen BEFEHL und "..." zwingend notwendig!!!
13
 * Prototypen der Funktionen in debug.h eintragen!
14
 */
15
const debug_commands f[] = {
16
  {"add ... add two numbers\n", cmd_add, "add <number> <number>\n"},
17
  {"mul ... multiply two numbers\n", cmd_mul, "mul <number> <number>\n"},
18
  {"help .. Command overview/help\n", cmd_help,"help [command]\n"},
19
  {"?  .... Command overview/help\n", cmd_help,"? [command]\n"},
20
  {"exit .. exit\n", cmd_exit, "x\n"}
21
};
22
23
#define COMMANDS 5      /* Anzahl der Kommandos in debug_commands f[] */
24
#define COMMAND_BUFFER 80  /* Eingabepuffer */
25
26
int exit_debug = 0;      /* Wird durch cmd_exit auf 1 gesetzt */
27
28
/* Hauptfunktion, wird nicht verlassen 
29
 *
30
 */
31
void debug(){
32
  char       debug[COMMAND_BUFFER];  /* Buffer für Debug-Kommando */
33
  unsigned char   i;
34
  
35
    printf("\n========= Console ==========\n");
36
    printf("Enter '?' for command overview\n");
37
  while(1){
38
    i = 0;
39
    
40
    printf("\n# ");    /* Weils toll aussieht */
41
           
42
    do{
43
      debug[i] = getch();
44
      putchar(debug[i]);      /* Echo */
45
      if(debug[i] == '\b' && i){  /* Backspace? */
46
        printf(" \b");      /* Leerzeichen ausgeben + Cursor zurück*/
47
        i--;          /* letztes Zeichen löschen */
48
      }
49
      else{
50
        i++;  
51
      }
52
      if(i >= COMMAND_BUFFER -1){  /* Kommando zu lang */
53
        i = 0;
54
        printf("\nCommand too long\n");
55
        continue;  
56
      }
57
    /* Schleife bei ENTER verlassen */  
58
    }while(debug[i-1] != '\r' && debug[i-1] != '\n');
59
    
60
    if(i == 1)      /* Kein Kommando eingegeben */
61
      continue;
62
    debug[i-1] = '\0';  /* Stringterminierung */
63
    putchar('\n');
64
    
65
    if(i > 0)
66
      parse_and_call_cmd(debug);  /* Kommando suchen und ausführen */
67
    if(exit_debug)            /* Wenn exit eingegeben */
68
      break;
69
  }  
70
}
71
72
/* Addiert zwei Zahlen */
73
void cmd_add(char argc, char **argv){
74
  int no1, no2;
75
  if(argc == 3){
76
    no1 = atoi(argv[1]);
77
    no2 = atoi(argv[2]);
78
    printf("%d + %d = %d", no1, no2, no1 + no2);
79
  }
80
}
81
82
/* Multipliziert zwei Zahlen */
83
void cmd_mul(char argc, char **argv){
84
  int no1, no2;
85
  if(argc == 3){
86
    no1 = atoi(argv[1]);
87
    no2 = atoi(argv[2]);
88
    printf("%d * %d = %d", no1, no2, no1 * no2);
89
  }
90
  else
91
    cmd_help(2,argv-1);    /* Kleiner Trick für Syntax-Ausgabe */
92
}
93
94
void cmd_exit(char argc, char **argv){
95
  exit_debug = 1;
96
}
97
98
void cmd_help(char argc, char **argv){
99
  unsigned char  i;
100
  unsigned char cmd_pos;
101
102
  if(argc == 2){            /* Kommandospez. Hilfe? */
103
    cmd_pos = is_dbg_command(argv[1]);  
104
    /* Debug-Kommando? Ja => Syntax ausgeben */
105
    if(cmd_pos != COMMANDS + 1 ){  
106
      printf("\n[..] - optional argument\n<..> - required argument\n");
107
      printf("\n%sSyntax: %s", f[cmd_pos].nptr, f[cmd_pos].syntax);
108
    }
109
    else  /* Kommando nicht gefunden */
110
      printf("Command not found\n");  
111
  }
112
  else{    /* Befehlsübersicht ausgeben */ 
113
    for(i = 0; i < COMMANDS; i++)
114
      printf(f[i].nptr);
115
  }
116
}
117
118
/* Extrahiert und zählt die Argumente, sucht das Kommando über
119
 * is_dbg_command und führt es dann aus.
120
 *
121
 */
122
void parse_and_call_cmd(char *cmdptr){
123
  unsigned char argc=0, cmd_pos;
124
  
125
#ifndef DYN_ARGS
126
  unsigned char *argv[MAX_ARGS];
127
#else
128
  unsigned char **argv = NULL;
129
  argv = (unsigned char **)malloc(sizeof(char **));
130
#endif
131
  
132
  argv[argc++] = cmdptr;  /* Kommando selbst in argv[0] */
133
  while(1){
134
    cmdptr++;
135
    if(*cmdptr == ' '){
136
      *cmdptr = '\0';  
137
      while(*++cmdptr == ' ');  /* Leerzeichen überspringen */
138
#ifdef DYN_ARGS
139
      argv = realloc(argv, (argc+1)*sizeof(char **));
140
#else
141
      if(argc > 9){
142
        printf("\nToo many arguments!\n");
143
        return;  
144
      }        
145
#endif
146
      argv[argc++] = cmdptr;
147
    }
148
    else if(*cmdptr == '\0'){
149
      break;
150
    }
151
  }
152
  cmd_pos = is_dbg_command(argv[0]);
153
  if(cmd_pos == COMMANDS + 1)          /* Befehl nicht gefunden */
154
    printf("\nUnknown command\n");
155
  else{
156
    (f[cmd_pos].fptr)(argc, (char **)argv); /*  Funktion aufrufen */
157
  }
158
#ifdef DYN_ARGS
159
  free(argv);
160
#endif
161
}
162
163
/* Sucht das Kommando in dem debug_commands-Struct und liefert
164
 * die Position im Struct zurück
165
 */
166
char is_dbg_command(char *cmdptr){
167
  unsigned char i=0, j=0, error;
168
  
169
  while(i < COMMANDS ){  /* Alle Kommandos durchsuchen */
170
    error = 0;
171
    while(f[i].nptr[j] != ' '){  
172
      if(f[i].nptr[j] != cmdptr[j]){
173
        error = 1;
174
        break;    /* Abweichung => nächsten Befehl testen */
175
      }
176
      j++;    
177
    }
178
      if(!(error == 0 && (cmdptr[j] == ' ' || cmdptr[j] == '\0') )){
179
      error = 1;
180
      i++;
181
      j=0;
182
    }  
183
    else        /* Kommando gefunden, error = 0 */
184
      break;
185
  }
186
  if(error==0)
187
    return i;
188
  else
189
    return COMMANDS+1;
190
}
191
192
/* Main, nur zum testen */
193
int main(){
194
    debug();
195
    return 0;
196
}
Ich glaube es ist relativ fehlerfrei und ist bei mir ständig im einsatz 
(hauptsächlich auf meinem AVR).

von I_ H. (i_h)


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.

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.