Funktionspointer in einer Struktur

OP #5292580
Lesenswert?

Nabend
1
typedef struct MENU {        // Daten-Struktur für Menüeintrag
2
  const unsigned char *text;  // Zeiger auf den anzuzeigenden String
3
//  int8_t previous;        // Zahl für vorherigen Menüeintrag, benötigt für mehrzeilige Displays
4
//  int8_t next;          // Zahl für nachfolgenden Menüeintrag, benötigt für mehrzeilige Displays
5
    void ( *fp )( void );   // Funktionszeiger
6
} MENU_ENTRY;              // Stuktur "MENU"mit Variablennamen "MENU_ENTRY"
An letzter Stelle von dieser Struktur kann Ich ein Funktion zuweisen.
Was macht man wenn man zig verschiedene Funktionen hat, mit 
verschiedenen Parametern?
Gibt es einen Trick oder brauche ich dafür wieder eine spez. Struktur?
Persönliche Seite #5292611
Lesenswert?

Jan H. schrieb:
> Was macht man wenn man zig verschiedene Funktionen hat, mit
> verschiedenen Parametern?
> Gibt es einen Trick oder brauche ich dafür wieder eine spez. Struktur?

Was willst Du damit machen? Willst Du diese unterschiedlichen Funktionen 
über ein und denselben Pointer aufrufen können?

Das kann nicht funktionieren, wie willst Du den Funktionen die 
unterschiedlchen Parameter übergeben?

Du brauchst schon einen zur jeweiligen Funktionssignatur passenden 
Funktionspointer.

Oder Du musst alle Funktionen mit variabler Parameterliste anlegen und 
als ersten festen Parameter eine Kennung mitliefern, wie die variable 
Parameterliste zu interpretieren ist.

Bist Du Dir sicher, daß das das ist, was Du willst?
#5292650
Lesenswert?

Für verschiedene Funktionen solltest du verschiedene Funktionszeiger 
anlegen. Soweit, so offensichtlich.

Wenn die Funktionen aber das gleiche tun sollen, weil du eine 
Abstraktion baust, dann machst du mit "die brauchen unterschiedliche 
Parameter und Typen" was falsch, denn dann ist das keine ordentliche 
Abstraktion.

Du kannst deine 30 verschiedenen Funktionen (mit verschiedenen Namen) 
aber in einer Union vereinigen und dann die Union in deine Struktur 
einbinden. Musst natürlich dann immer durch den korrekten Zeiger 
aufrufen, sonst passiert Murks.
#5292688
Lesenswert?

Code hat Achim schon geschrieben, danke.
Aber erkläre mal lieber, was du eigentlich vorhast?

"LCD Struktur" ist erstmal nichtssagend, und deine Frage klingt eher 
danach, als ob du groben Unfug treiben willst, ohne es zu wissen. Da 
gibt es bestimmt bessere Ansätze.
#5292929
Lesenswert?

Mit so wenig Information schwer zu sagen, ob das hier vernünftig wäre, 
aber deiner Beschreibung nach könntest Du die Funktionen auch variadisch 
implementieren.

Sie hätten dann für den Compiler allesamt dieselbe Parameterliste und 
die Frage nach der Union stellt sich nicht.
Gast #5293003
Lesenswert?

Datum: 26.01.2018 06:50
> könntest Du die Funktionen auch variadisch
> implementieren.

Datum: 26.01.2018 06:52
> Wie stellt man das an?

Bei dem (2 Minuten) Lerneifer, und dieser Googlehemmung, vermute ich 
mal, dass das Ganze von Anfang an ein fataler Irrtum ist.

> Glaube nicht, dass dich der von dir eingeschlagene Weg zum
> Ziel führt . Häufig kommen Leute an einem Punkt nicht
> mehr weiter und bitten um Hilfe zu einem bestimmten Schritt,
> ohne zu bemerken, dass der gewählte Pfad falsch ist. Es
> kann viel Mühe kosten, dann doch noch zum Ziel zu gelangen.
Aus: http://phpforum.de/forum/showthread.php?t=188250
OP #5293072
Lesenswert?

Und wie Referenziere ich jetzt auf meine Funktionen die Ich aufrufen 
will?
1
union ufp
2
{
3
  void (*fnc0)(uint8_t);
4
  void (*fnc)(void);
5
};
6

7
typedef struct
8
{
9
  char *Name;
10
  uint8_t numb;
11
  union ufp fp;
12
}menue_t;
13

14
menue_t mainMenue[]=
15
{
16
  {"Uhrzeit stellen"  ,0},
17
  {"Datum stellen"  ,1},
18
  {"Bluetooth"    ,2},
19
  {"Neustart"      ,3},
20
  {"Exit"        ,4},      
21
};
22

23
mainMenue[0].fp.fnc(); // hier sollte z.B die Funktion .: setTime() aufgerufen werden...
Moderator Persönliche Seite #5293108
Lesenswert?

Statt einer Union über eine Latte von verschiedenen Funktionszeigern 
könnte man alternativ auch einen einzigen Funktionszeiger verwenden, wo 
als Parameter ein Pointer auf eine Struct/Union übergeben wird, welche 
alle möglichen Parameter abdeckt.

Die jeweils aufgerufene Funktion pickt sich dann die nötigen Parameter 
aus der Struct heraus. Das können je nach Kontext dann sogar 
verschiedene sein.
#5293142
Lesenswert?

Die Parameter müssen von irgendwo her kommen. Ich würde sie in ein 
struct packen, und wrapper funktionen erstellen:
1
struct menu {
2
  const char* name;
3
  void (*func)(struct menu* menu);
4
  void* params;
5
};
6

7
void some_function(int a, int b);
8

9
struct some_function_menu_params {
10
  int a;
11
  int b;
12
};
13

14
void some_function_menu_wrapper(struct menu* menu){
15
  struct some_function_menu_params* params = menu->params;
16
  some_function(params->a, params->b);
17
}
18

19
struct menu mainMenue[]=
20
{
21
  {"Tue was", some_function_menu_wrapper, (struct some_function_menu_params[]){{1,2}}},
22
  {"Tue was 2", some_function_menu_wrapper, (struct some_function_menu_params[]){{2,3}}},
23
};

Das könnte man teils mit Makros noch etwas vereinfachen.
Persönliche Seite #5293179
Lesenswert?

Jan H. schrieb:
> Markus F. schrieb:
>> aber deiner Beschreibung nach könntest Du die Funktionen auch variadisch
>> implementieren.
> Wie stellt man das an?
1
#include <stdint.h>
2

3
extern int sum (uint8_t, ...);
4

5
#include <stdarg.h>
6

7
int sum (uint8_t n_args, ...)
8
{
9
    va_list args;
10
    va_start (args, n_args /* Last named arg */);
11

12
    int s = 0;
13
    for (uint8_t n = 0; n < n_args; n++)
14
    {
15
        s += va_arg (args, int);
16
    }
17

18
    va_end (args);
19
    return s;
20
}
21

22
int (*pfunc)(uint8_t, ...) = sum;
23

24
int main()
25
{
26
    return pfunc (3, 11, 22, 33) + sum (1, -66);
27
}

Hier mit einer variadischen Funktion sum() zusammen mit ihrem Aufruf, 
einmal direkt und einmal indirekt über einen Funktionszeiger.

sum() bekommt als 1. Argument die Anzahl der zu addierenden Summanden, 
welche alle vom Typ int sind.  Das "..." ist keine Obfuscation, sondern 
ein C/C++ Token.

Eine varargs-Funktion muss wissen, wie sie die übergebenen Argumente zu 
interpretieren hat, und wieviel Argumente dies sind.  Bei printf etc. 
wird dies z.B. über den Formatstring übermittelt.  Im Beispiel werden 
nur int addiert, daher muss diese Info nicht übermittelt werden sondern 
nur die Anzahl der Argumente.  Das wäre anders, wenn es z.B. ein Mix aus 
int, long, uint32_t, int* und float wäre.

Die Funktion selbst wird etwas komplizierter, dafür sind die Aufrufe 
einfacher hinzuschreiben.  Bei der Union-Lösung muss man z.B. abhängig 
von dem auszurufenden Prototypen eine Fallunterscheidung machen, und 
abhängig von den Fällen dann die Funktion aufrufen.  Tatsächlich ist es 
da eher zweifelhaft, den Funktionszeiger per Type-Punning 
zurechtzubiegen, stattdessen könnte man auch direkt einen Cast 
verwenden, was den Vorteil hat, dass die Information an einer Stelle 
ist und nicht verteilt auf Union und Code:
1
#include <stdint.h>
2
#include <stdlib.h>
3

4
int sum_f (void *f, uint8_t n_args, int arg1, int arg2, int arg3)
5
{
6
    int s = 0;
7
    switch (n_args)
8
    {
9
        default: abort();
10
        case 0: return ((int(*)(void)) f) ();
11
        case 1: return ((int(*)(int)) f) (arg1);
12
        case 2: return ((int(*)(int,int)) f) (arg1, arg2);
13
        case 3: return ((int(*)(int,int,int)) f) (arg1, arg2, arg3);
14
    }
15
}
#5293738
Lesenswert?

> void handle7SEGMENT(void)
> void led_show_temp(int16_t tempIn, uint8_t sense)
> uint8_t *i2c_scan(uint8_t *buffer)

Diese drei Funktionen tun vollkommen unterschiedliche Dinge.

Wie wäre es, wenn du einfach DREI Funktionszeiger in deine Struct tust 
und einfach die richtige Funktion aufrufst?
Gast #5296219
Lesenswert?

MeineZweiCent schrieb:
> Wo ist das Problem mit dem "Union aller Funktionspointer" Ansatz?

Dass man in Nachhinein nicht mehr feststellen kann, welche 
Funktionssignatur denn jetzt die richtige ist. Damit ist ein solcher 
Zeiger recht nutzlos.

Es sei denn, man führt auch noch die Signatur Information mit, damit man 
das zuordnen kann.
Gast #5296265
Lesenswert?

Um den Ansatz zu Ende zu bringen und die Frage des TE zu beantworten, 
trotzdem:


#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

union ufp
{
  void (*fpn0)(void);
  void (*fpn1)(int16_t,uint8_t);
  uint8_t*(*fpn2)(uint8_t*);
};

typedef struct
{
  char *Name;
  uint8_t numb;
  union ufp fp;
}menue_t;


void HandleSevenSegment(void){
  printf("I will handle the seven segment display\n\n");
  return;
}
void LEDShowTemp(int16_t tempIn, uint8_t sense){
  printf("And I will do some fancy LED show temperature stuff\n\n");
}
uint8_t *I2CScan(uint8_t *buffer){
  printf("I usually do not use I2C bus\n\n");
  return 0;
}

int main(void){

uint8_t* arg;
uint8_t* ret;

  menue_t mainMenue[]=
  {
    {"Uhrzeit stellen",  0,  .fp.fpn0 = &HandleSevenSegment},
    {"Datum stellen",  1,  .fp.fpn1 = &LEDShowTemp},
    {"Bluetooth",  2,  .fp.fpn2 = &I2CScan},
    {"Neustart",  3,  .fp.fpn1 = &LEDShowTemp},
    {"Exit",  4,  .fp.fpn0 = &HandleSevenSegment},
  };

  mainMenue[0].fp.fpn0();
  mainMenue[1].fp.fpn1(3,6);
  ret = mainMenue[2].fp.fpn2(arg);

  return 0;
}
Gast #5296272
Lesenswert?

Boa macht ihr Sachen.
Das wird mit der Zeit vollkommen unübersichtlich und schwer wartbar. Die 
Variablen müssen ja trotzdem alle irgendwie (modul-)global sein damit 
das funktioniert.
Und wehe wenn jemand das falsche Funktions-Argument initialisiert....
Vorschlag:
nur void funktionpointer nutzen und den Rest in der entsprechenden 
Funktion auflösen....
1
typedef struct MENU {
2
   const char *text;
3
   void ( *fp )( void );   
4
} MENU_ENTRY;  
5

6
void DlgSetTime()   { setTime(&xtime); }
7
void DlgSetDate()   { setDate(&xDate); }
8
void DlgBluetooth() { bluetooth(&bt1, $bt2); }
9

10
MENU_ENTRY menue[3]=
11
{
12
  {"Uhrzeit stellen"  , DlgSetTime },
13
  {"Datum stellen"    , DlgSetDate },
14
  {"Bluetooth"        , DlgBluetooth }    
15
};


Wer natürlich C++ sein eigen nennt, darf Lambda expressions nutzen 
(since C++11...)
Beispiel:
1
  {"Bluetooth"        , []{bluetooth(%bt1, &bt2);} }
Gast #5296285
Lesenswert?

Weitere Nachteil der Vorgehensweise ala
mainMenue[2].fp.fpn2(arg);

Der Menücode muss die Variablen kenne, die übergeben werden.
Beim void Funktionpointer können alle Variablen gekapselt bleiben, 
sprich:
die Bluetooth Daten sind modulintern im Bluetooth Code. Nur die 
Einsprung Funktion muss public sein.
OP #5296451
Lesenswert?

Klaus schrieb:
> typedef struct MENU {
>    const char *text;
>    void ( *fp )( void );
> } MENU_ENTRY;
>
> void DlgSetTime()   { setTime(&xtime); }
> void DlgSetDate()   { setDate(&xDate); }
> void DlgBluetooth() { bluetooth(&bt1, $bt2); }
>
> MENU_ENTRY menue[3]=
> {
>   {"Uhrzeit stellen"  , DlgSetTime },
>   {"Datum stellen"    , DlgSetDate },
>   {"Bluetooth"        , DlgBluetooth }
> };

Hallo Klaus,

das ist eine interessante Lösung. Gibt es einen speziellen Namen für 
diese Vorgehensweise? Irgendwo mit einem Link evtl. beschrieben?
Habe Ich bis jetzt noch nicht gesehen, diese Art von Zuweisungen.
Gast #5296485
Lesenswert?

Moin, was meinst Du mit Link? Zum Thema lambda expressions al "[]()" ?
Jedes gute C++ Buch, oder 
http://en.cppreference.com/w/cpp/language/lambda

Ansonsten habe ich die normale Variante mit void DlgXyz() schon mehrfach 
in GUIs eingesetzt (C Systeme).

In der Literatur wird ab und zu der Begriff "Callback" Funktion genutzt. 
Sprich das Menü ruft zurück wenn etwas zu tun ist. (Öfters im 
Zusammenhang mit Fenstermanager, wenn diese z.B. ein Neuzeichnen eines 
Widgets anfordern …)
#5296513
Lesenswert?

Klaus schrieb:
> nur void funktionpointer nutzen und den Rest in der entsprechenden
> Funktion auflösen....

Interessanter Ansatz. So muß ich für jede einzustellende Variable eine 
extra Funktion schreiben.

Der Gegenentwurf ist eine einzelne komplizierte Funktion, die weiträumig 
parametriert werden kann.

Sieht dann so aus:
1
  typedef enum
2
    {
3
        nothing,
4
    voidFcn,
5
    uint8,
6
    uint16,
7
    int8,
8
    int16,
9
    #if MENU_SUPPORT_FLOAT
10
        float32,
11
    #endif
12
    endmarker,
13
    exitMenu,
14
  }
15
  type_t;
16

17

18

19
    // Menue-Eintraege: Zu startende Funktionen, zu aendernde Variablen
20
    // Die Reihenfolge macht es uebersichtlicher, Menutext und Variableninhalt separat
21
    // zu speichern.
22
    // Ein Eintrag sieht dann z.B. wie folgt aus:
23
    // { FLSTEXT1, .Uint8={ .type=uint8, .ptr=&var, .min=1, .max=255, .inc=5} }
24
    // { FLSTEXT2, .VoidFcn={ voidFcn, &foo }       }
25
    __extension__ typedef struct
26
    {
27
      constflash char *menutext; // Menueeintrag/Funktionsname
28

29
        union
30
      {
31
        // Generisch
32
        struct
33
        {
34
          type_t type;               // Datentyp:
35
                         // Bei function pointer wird die Funktion
36
                         // gestartet, bei Zeigern auf normale Daten
37
                         // der Datentyp im Menue bearbeitet.
38
        } All;
39

40
        // Container void-void-Funktionen
41
        struct
42
        {
43
          type_t type;               // Datentyp
44
          voidFcn_t ptr;             // Funktionszeiger
45
        } VoidFcn;
46

47
        // Container uint8_t-Variable
48
        struct
49
        {
50
          type_t type;               // Datentyp
51
          uint8_t *ptr;              // Zeiger auf Variablentypen
52
          uint8_t min;               // Untere Grenze
53
          uint8_t max;               // Obere Grenze
54
          uint8_t inc;               // Inkrement
55
        } Uint8;
56

57
        // Container int8_t-Variable
58
        struct
59
        {
60
          type_t type;               // Datentyp
61
          int8_t *ptr;               // Zeiger auf Variablentypen
62
          int8_t min;                // Untere Grenze
63
          int8_t max;                // Obere Grenze
64
          int8_t inc;                // Inkrement
65
        } Int8;
66

67
        // Container uint16_t-Variable
68
        struct
69
        {
70
          type_t type;               // Datentyp
71
          uint16_t *ptr;             // Zeiger auf Variablentypen
72
          uint16_t min;              // Untere Grenze
73
          uint16_t max;              // Obere Grenze
74
          uint16_t inc;              // Inkrement
75
        } Uint16;
76

77
        // Container int16_t-Variable
78
        struct
79
        {
80
          type_t type;               // Datentyp
81
          int16_t *ptr;              // Zeiger auf Variablentypen
82
          int16_t min;               // Untere Grenze
83
          int16_t max;               // Obere Grenze
84
          int16_t inc;               // Inkrement
85
        } Int16;
86

87
            #if MENU_SUPPORT_FLOAT
88
        // Container float32_t-Variable
89
        struct
90
        {
91
          type_t type;               // Datentyp
92
          float32_t *ptr;            // Zeiger auf Variablentypen
93
          float32_t min;             // Untere Grenze
94
          float32_t max;             // Obere Grenze
95
          float32_t inc;             // Inkrement
96
        } Float32;
97
            #endif
98

99
        };
100
    } Menuitem_t;                   // Struct wird terminiert mit type=endmarker

und wird so benutzt:
1
constflash char MT_TESTBENCH_HEADLINE[] = "Stepper Testbench";
2
constflash char MT_TESTBENCH_VELOCITY[] = "Velocity   :%6i";
3
constflash char MT_TESTBENCH_STEPMODE[] = "StepMode   :%6i  "; 
4
constflash char MT_TESTBENCH_CURRENT[]  = "Current    :%6.1f mA";
5

6
constflash Menuitem_t TestbenchMenuData[] =
7
{
8
    { MT_TESTBENCH_VELOCITY, .Int16 = {int16, &(TbdAct.velocity), .min = INT16_MIN, .max = INT16_MAX, .inc = 1} },
9
    { MT_TESTBENCH_STEPMODE, .Uint8 = {uint8, &(TbdAct.stepMode), .min = 0,         .max = 4,         .inc = 1} },
10
    { MT_TESTBENCH_CURRENT , .Float32 = {float32 , &(TbdAct.current_mA), .min=.0f, .max=4000.0f, .inc=31.25f  } },
11
    { MT_NULL              , .All   = {nothing                                                                } }, // Leerzeile
12
    { MT_QUIT        , .All   = {exitMenu                                                               } },
13
    { MT_NULL              , .All   = {endmarker                                                              } },
14
};

Irgendwo muß die Komplexität immer bleiben.
Gast #5296537
Lesenswert?

Bitte

Es gibt natürlich Variationen. In komplexen Menüs hat man mehrere 
Aktionen.
Ich habe auch schon obige Variante mit vielen structs genutzt.
Aber dann habe ich meistens C++ im Einsatz, lege eine Basisklasse an, 
und leite entsprechende Klassen für Datentypen, Untermenüs etc. ab.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren