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?
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?
> Bist Du Dir sicher, daß das das ist, was Du willst?
Ich möchte eine Art LCD Struktur anlegen. Nicht jede Funktion hat die
gleiche Anzahl an Parameter und Typ.
Daher müsste Ich verschiedene Parameter übergeben.
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.
S. R. schrieb:> 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.
Hast du ein Pseudo Kode für mich?
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.
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.
Markus F. schrieb:> 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.
Wie stellt man das an?
Frage waere ja noch von wem diese Funktionen aufgerufen werden, und wie
dieser denn uwberhaupt die Paramerer kennt. Kannst Du vielleicht mal
Beispielr fuer die unterschiedluchen Funktionen geben?
Dumdi D. schrieb:> Frage waere ja noch von wem diese Funktionen aufgerufen werden,> und wie> dieser denn uwberhaupt die Paramerer kennt. Kannst Du vielleicht mal> Beispielr fuer die unterschiedluchen Funktionen geben?
Jan H. schrieb:> void handle7SEGMENT(void)> void led_show_temp(int16_t tempIn, uint8_t sense)> uint8_t *i2c_scan(uint8_t *buffer)
Ok. Und woher soll die aufrufende Funktion wissen welche Daten sie da
reinschreiben soll? D.h. Du musst sowieso ajtiv Fallunterscheidungej
machen. Oder wrapper Funktionen mit gleicher Signatur verwenden.
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
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.
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
externintsum(uint8_t,...);
4
5
#include<stdarg.h>
6
7
intsum(uint8_tn_args,...)
8
{
9
va_listargs;
10
va_start(args,n_args/* Last named arg */);
11
12
ints=0;
13
for(uint8_tn=0;n<n_args;n++)
14
{
15
s+=va_arg(args,int);
16
}
17
18
va_end(args);
19
returns;
20
}
21
22
int(*pfunc)(uint8_t,...)=sum;
23
24
intmain()
25
{
26
returnpfunc(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:
...und bei einer LCD-Funktion kann man gleich einen printf-ähnlichen
Ansatz wählen, um Text auszugeben oder Kommandos wie den Cursor an eine
Bestimmte Position zu setzen, beispielsweise per "%@" was dann 2
unsigned Argumente für Zeilen- und Spaltenposition erwartet.
> 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?
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.
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;
}
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
typedefstructMENU{
2
constchar*text;
3
void(*fp)(void);
4
}MENU_ENTRY;
5
6
voidDlgSetTime(){setTime(&xtime);}
7
voidDlgSetDate(){setDate(&xDate);}
8
voidDlgBluetooth(){bluetooth(&bt1,$bt2);}
9
10
MENU_ENTRYmenue[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:
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.
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.
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 …)
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
typedefenum
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
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.
Der Ansatz von Walter Tarpan ist auch gut, ich würde aber die Union
vermeiden und etwas objektorientierter aufbauen. Komm aber frühestens
heute Abend dazu ein Beispiel zu machen.