Forum: Compiler & IDEs C++ Definitions Zwickmühle


von D. C. (joker89)


Lesenswert?

Hallo,
ein paar Zeilen sagen mehr als ...->
1
typedef void (*MenuFnct) (*ptrMenu);
2
3
struct MenuEntry
4
{
5
    char*               text;
6
    MenuFnct            function;
7
    Flags               flags;
8
    struct MenuEntry    *subMenu;
9
    uint8_t             storage;
10
};
11
12
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.

Gruß

: Verschoben durch User
von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Du suchst eine Vorwärtsdeklaration. Unter diesem Stichwort (oder dem 
englischen Äquivalent forward declaration) findest Du das in Deinem C- 
bzw. C++-Buch beschrieben.

: Bearbeitet durch User
von g457 (Gast)


Lesenswert?

> [..] Vorwärtsdeklaration [..]

Das ist nur die erste Hälfte. Der TO hat noch einen Fehler bei MenuFnct 
eingebaut, der leicht verwirrende Fehlermeldung verursacht :-)

von Daniel A. (daniel-a)


Lesenswert?

1
struct MenuEntry;
2
typedef void (*MenuFnct) (struct MenuEntry*);
3
4
struct MenuEntry {
5
...

von Rolf M. (rmagnus)


Lesenswert?

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
typedef void (*MenuFnct) (struct MenuEntry*);

> 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.

von D. C. (joker89)


Lesenswert?

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ß

von Arc N. (arc)


Lesenswert?

Das sollte nicht weit entfernt von Vorwärtsdeklarationen im Buch 
stehen...
extern struct MenuEntry MainMenu[];

von Daniel A. (daniel-a)


Lesenswert?

Suchst du etwas wie:
1
int x[] = {1,2,3,4};
2
int x_count = sizeof(x)/sizeof(*x); // ist 4

von ProjektX (Gast)


Lesenswert?

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
 typedef void (*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
 typedef void (*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
 void foo ( uint8_t argFcnt , ...);

Eine Menüstruktur wird deklariert:
1
 MENU_ENTRY menu;

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
void foo(uint32 Flags, ...)
2
{
3
   // Zeigerdeklaration für den Stackzugriff
4
   va_list argPointer;
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.

von Daniel A. (daniel-a)


Lesenswert?

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.

: Bearbeitet durch User
von ProjektX (Gast)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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
struct MenuEntry Settings[] =
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:
1
#define SET_MENU_ADM(x)  x,(sizeof(x)/sizeof(x[0]), #x
2
3
typedef struct
4
{
5
   MenuEntry *p;
6
   int        n;
7
   const char *MainTitle;
8
}TMenuAdm;
9
10
TMenuAdm SettingsAdm = {SET_MENU_ADM(Settings)};

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.

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.