mikrocontroller.net

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


Autor: D. C. (joker89)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
ein paar Zeilen sagen mehr als ...->


typedef void (*MenuFnct) (*ptrMenu);

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.

Gruß

: Verschoben durch Moderator
Autor: Rufus Τ. F. (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
2 lesenswert
nicht 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 Moderator
Autor: g457 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> [..] Vorwärtsdeklaration [..]

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

Autor: Daniel A. (daniel-a)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
struct MenuEntry;
typedef void (*MenuFnct) (struct MenuEntry*);

struct MenuEntry {
...

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht 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:
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.

Autor: D. C. (joker89)
Datum:

Bewertung
0 lesenswert
nicht 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ß

Autor: Arc N. (arc)
Datum:

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

Autor: Daniel A. (daniel-a)
Datum:

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

Autor: ProjektX (Gast)
Datum:

Bewertung
-1 lesenswert
nicht 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
 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:
 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:
 void foo ( uint8_t argFcnt , ...); 

Eine Menüstruktur wird deklariert:
 MENU_ENTRY menu; 

Der Zeiger auf unsere Funktion foo wird in menu gesichert:
 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:
 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:

void foo(uint32 Flags, ...)
{
   // Zeigerdeklaration für den Stackzugriff
   va_list argPointer;

   // Zeiger wird so initialisiert, dass er hinter das letzte
   // bekannte Argument auf dem Stack zeigt
   va_start(argPointer, Flags);


   // Man holt vom Stack eine Variable eines vorher nicht vereinbarten
   // Datentyps. Nach der Operation wird der Zeiger inkrementiert, um 
   // ggf. weitere Argument vom Stack holen zu können
   menu_p = va_arg(argPointer, MENU_ENTRY*);

   // Mit dieser Definition kannst du dir nun eine Funktion aufrufen, 
   // deren Name unbekannt ist, von der jedoch der Speicherort des
   // diese Funktion weisenden Zeigers bekannt ist. Sprich, eine universelle
   // Schablone zum Aufrufen von Ober-/Untermenüs, ohne deren Namen zu kennen


   //Der Argumentzeiger wird auf NULL gesetzt
   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.

Autor: Daniel A. (daniel-a)
Datum:

Bewertung
2 lesenswert
nicht 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
Autor: ProjektX (Gast)
Datum:

Bewertung
-1 lesenswert
nicht 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.

Autor: Achim S. (achs)
Datum:

Bewertung
0 lesenswert
nicht 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; ?):
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 },
    { 0}
};
....
   while(p=Settings, p->Title, p++)
   {
       InsertSubMenu(p);
   }

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:

#define SET_MENU_ADM(x)  x,(sizeof(x)/sizeof(x[0]), #x

typedef struct
{
   MenuEntry *p;
   int        n;
   const char *MainTitle;
}TMenuAdm;

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.

: Bearbeitet durch User

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.