das Problem liegt bei *ptrMenu ich bekomme natürlich die Meldung das er
oben *ptrMenu noch nicht kennt. Der Funktionspointer muss einen Pointer
übergeben bekommen können der den Wert des Struct MenuEntry tragen kann.
Gruß
Du suchst eine Vorwärtsdeklaration. Unter diesem Stichwort (oder dem
englischen Äquivalent forward declaration) findest Du das in Deinem C-
bzw. C++-Buch beschrieben.
> [..] Vorwärtsdeklaration [..]
Das ist nur die erste Hälfte. Der TO hat noch einen Fehler bei MenuFnct
eingebaut, der leicht verwirrende Fehlermeldung verursacht :-)
D. C. schrieb:> Hallo,> ein paar Zeilen sagen mehr als ...->>> typedef void (*MenuFnct) (*ptrMenu);
Was soll das sein? (*ptrMenu) sieht nicht wie eine korrekte
Parameterliste aus. Meintest du:
1
typedefvoid(*MenuFnct)(structMenuEntry*);
> struct MenuEntry> {> char* text;> MenuFnct function;> Flags flags;> struct MenuEntry *subMenu;> uint8_t storage;> };>> MenuEntry *ptrMenu = NULL;>> das Problem liegt bei *ptrMenu ich bekomme natürlich die Meldung das er> oben *ptrMenu noch nicht kennt. Der Funktionspointer muss einen Pointer> übergeben bekommen können der den Wert des Struct MenuEntry tragen kann.
Dann gibt das doch auch so an.
Also es funktioniert bisher prima, aber wie mache ich das bei einem
Array wenn die größe nicht kenne oder es nicht definieren will kann ich
dem compiler das irgendwie beibringen hey es gibt ein array of struct
aber wie groß es ist sag ich dir später. MainMenu gibt es oben noch
nicht und mit struct MenuEntry MainMenu fehlt ihm die info es wird ein
array of struct die größe weiß ich aber evtl oben noch nicht ?
struct MenuEntry Settings[] =
{
{ "Batterie BEEPS", AutoSearch, { 0 , 0 } , NULL , 0 },
{ "Calibrate RSSI", BandScanner, { 0 , 0 } , NULL , 0 },
{ "Video Input", ManualMode, { 0 , 0 } , NULL , 0 },
{ "Back", drawMenu , { 1 , 0 } , MainMenu , 0 },
};
struct MenuEntry MainMenu[] =
{
{ "Auto Search", AutoSearch, { 0 , 1 } , NULL , 0},
{ "Band Scanner", BandScanner, { 0 , 0 } , NULL , 0 },
{ "Manual Mode", ManualMode, { 0 , 0 } , NULL , 0 },
{ "Settings", drawMenu , { 0 , 0 } , Settings , 0 },
{ "Exit", exit , { 1 , 0 } , NULL , 0 },
};
Gruß
Eine der Möglichkeiten, die sehr häufig übersehen werden, aus welchem
Grund auch immer: Wenn du eine Funktionsschablone erstellst, in deinem
Fall die
1
typedefvoid(*MenuFnct)(*ptrMenu);
willst du die dem Compiler unbekannte MENU_ENTRY-Struktur reinwürgen. Es
gibt dafür eine universelle Lösung: Du kannst sogennante Funktionen mit
optionalen Parametern übergeben. Sprich, Übergabeparameter, deren Werte
und Datentyp gar nicht bekannt sind - und erst zur Laufzeit bestimmt
werden.
Statt deines MenuEntry-Structs im anderen Struct erstellst du einen
generischen Zeiger void *menuAddr_p, da an es an dieser Stelle nicht
möglich ist, einen MENU_ENTRY-zeiger zu erstellen, da die Struktur dafür
ja erst deklariert werden muss. Ich würde zwei void-Zeiger erstellen,
für den Übergang zum Übermenü und zum Untermenü.
Ein typedef würde z.B. so aussehen:
1
typedefvoid(*MenuFnct)(uint32_t,...);
Damit kannst du irgendwelche festen Parameter übergeben (irgendwelche
individualisierten Werte für jeden Menüeintrag, wie
aktiviert/deaktiviert, 0...10 oder so). Gehen wir mal von aus, dass
MenuFcnt ein Zeiger auf die aufzurufende Funktion ist, z.B. beim Drücken
auf den OK-Button.
Und dann deklarierst du eine Funktion foo:
1
voidfoo(uint8_targFcnt,...);
Eine Menüstruktur wird deklariert:
1
MENU_ENTRYmenu;
Der Zeiger auf unsere Funktion foo wird in menu gesichert:
1
menu.function=foo;
Nun rufen wir die Funktion auf, deren Adresse in menu.function gesichert
wurde. Also in diesem Fall foo() mit optionalem Parameter *MenuFcnt:
1
menu.function(menu.Flags,menu.menuAddr_p);
Wie du unschwer erkennen kannst, haben wir hier den Zeiger übergeben,
der in unserem Fall schon auf die foo() zeigt.
Nun muss die Funktion foo() natürlich den optionalen Parameter
verarbeiten können. Dafür brauchst du die stdarg-Bibliothek. Diese kann
direkt auf den Stack zugreifen.
Ein Beispiel des foo()-Aufbaus:
1
voidfoo(uint32Flags,...)
2
{
3
// Zeigerdeklaration für den Stackzugriff
4
va_listargPointer;
5
6
// Zeiger wird so initialisiert, dass er hinter das letzte
7
// bekannte Argument auf dem Stack zeigt
8
va_start(argPointer,Flags);
9
10
11
// Man holt vom Stack eine Variable eines vorher nicht vereinbarten
12
// Datentyps. Nach der Operation wird der Zeiger inkrementiert, um
13
// ggf. weitere Argument vom Stack holen zu können
14
menu_p=va_arg(argPointer,MENU_ENTRY*);
15
16
// Mit dieser Definition kannst du dir nun eine Funktion aufrufen,
17
// deren Name unbekannt ist, von der jedoch der Speicherort des
18
// diese Funktion weisenden Zeigers bekannt ist. Sprich, eine universelle
19
// Schablone zum Aufrufen von Ober-/Untermenüs, ohne deren Namen zu kennen
20
21
22
//Der Argumentzeiger wird auf NULL gesetzt
23
va_end(argPointer);
Falls es dabei Verständnisprobleme gibt, würde ich dir ans Herz legen,
einen Schritt zurückzugehen und dich etwas mehr in die Grundlagen
einzuarbeiten. Hoffe ansonsten, das bringt dich (und vllt. auch andere)
weiter in ihren Projekten. Viel Erfolg.
ProjektX schrieb:> Statt deines MenuEntry-Structs im anderen Struct erstellst du einen> generischen Zeiger void *menuAddr_p, da an es an dieser Stelle nicht> möglich ist, einen MENU_ENTRY-zeiger zu erstellen, da die Struktur dafür> ja erst deklariert werden muss.
Lies den Thread doch nochmal durch. Vorwärtsdeklaration sind im ersten
Beitrag bereits genannt, und ein Beispiel steht auch direckt darunter,
geht also bestens. Es gibt sinnvolle Anwendungsfälle für void Pointer
und Varadic functions, dieser ist keiner davon. Void pointer nimmt man
nur, wenn der Typ auf den etwas zeigt unbekannt ist. z.B. beim
Durchreichen eines beliebigen Arguments zu einer callback funktion.
Varadic functions sind eigentlich überflüssig, können aber sinvoll sein,
wenn die Anzahl der Argumente und dessen Typ variabel ist. Bei diesem
Menu ist weder der Datentyp, noch die Anzahl unbekannt, also gibt keinen
Grund void pointer oder varadic functions zu verwenden, diese machen den
Code nur unnötig kompliziert.
Wenn du meinst, dass das sinnlos ist, dann wird auch sicherlich so sein.
Kannst ja dann mal dein komplett generisches Menü zeigen, mit während
der Laufzeit dazuladbaren und Modulen.
Bisschen den Horizont erweitern würde helfen.
D. C. schrieb:> die größe weiß ich aber evtl oben noch nicht ?
die einfachste Art zur Traversierung über Arrays unbekannter Größe ist
die Festlegung, die Liste mit einer 0 (oder einem special-Value) im
ersten Element abzuschließen. Hier z.B. der erste Text (const char
Title; ?):
1
structMenuEntrySettings[]=
2
{
3
{"Batterie BEEPS",AutoSearch,{0,0},NULL,0},
4
{"Calibrate RSSI",BandScanner,{0,0},NULL,0},
5
{"Video Input",ManualMode,{0,0},NULL,0},
6
{"Back",drawMenu,{1,0},MainMenu,0},
7
{0}
8
};
9
....
10
while(p=Settings,p->Title,p++)
11
{
12
InsertSubMenu(p);
13
}
Leider erfordert das Dispziplin, einmal vergessen und tschüss... Die
zweitbeste Art ist eine Verwaltungsstruktur, die zur Compilezeit mit
einem Pointer auf das Array und der Größe gefüllt wird. Am Besten per
Makro unterstützt:
Zwei Vorteile:
- Settings selber kann lokal bleiben (static)
- es können weitere Information mitgegeben werden, z.B. ein Menü-Titel
wie hier im Beispiel (der ja genau nicht in den einzelnen Elementen oder
im Variablennamen stecken kann)
Und übrigens, der wichtigste Rat hier im Thread bleibt der von Daniel.
Daniel A. schrieb:> Es gibt sinnvolle Anwendungsfälle für void Pointer> und Varadic functions, dieser ist keiner davon
nicht dass Du da einer bedauerlichen Sackgasse folgst.