Forum: Mikrocontroller und Digitale Elektronik Programmieren einer Menüstruktur in C


von Stefan S. (mphunter)


Lesenswert?

Tach zusammen!

Hab da ein Problem bei der Realisierung meiner Menüstruktur in C.
Ich habe vier Taster zur Bedienung meiner Menüstruktur. Ich habe ein 
Grundmenü was sich wiederrum in mehrere Untermenüs aufsplittet. Kann ich 
diese Menüführung nur über eine if Anweisung mit einer while Schleife 
aufbauen oder weiß jemand von Euch einen besseren Rat?
Bin dankbar für jeden Ratschlag!

von Olli (Gast)


Lesenswert?

Deine Frage ist in etwa so präzise wie "Ich möchte ein großes Haus 
bauen. Wie viele Fenster brauch ich?"

von Der M. (steinadler)


Lesenswert?

Natürlich kannst du das,

du speicherst in einer Variable den Tastencode (0-3) und wertest dann 
mit Hilfe von "switch" - "case" das ganze aus.

if geht auch; ich persönlich finde switch case schöner.
Zumindest wenn mehr als ein Befehl ausgeführt werden soll.

von Karl H. (kbuchegg)


Lesenswert?

Funtionspointer wären auch eine Alternative.

von whitenoise (Gast)


Lesenswert?

...ja, oder eine funktion für alles, der über gibst du dann eine liste 
von funktionspointern..

gruß,
whitenoise

von Latissimo (Gast)


Lesenswert?

Ich wäre für switch!

für jede Auswahlmöglichkeit(zb. im Hauptmenü sind 6 untermenüs 
aufrufbar)
eine switch-Anweisung nutzen. Im Untermenü dann wieder ein switch

switch(hautpmenü)

switch(untermenü1)
.
.
.

ich weiß, das mit den üs geht so net, also nicht auf mir rumhacken... ;)

von I_ H. (i_h)


Lesenswert?

Dazu hab ich mir irgendwann mal folgendes zusammenprogrammiert:
1
// menu entry structure
2
struct UImenuEntry
3
{
4
  const char* name;  // entry name
5
  int id;      // entry ID
6
};
7
8
9
// simple menu structure
10
struct UImenu
11
{
12
  const char* menuName;    // menu name
13
  int mid;      // menu ID
14
  
15
  char entries;      // entry count
16
  struct UImenuEntry e[];    // entries  
17
};

Im Einsatz sieht das dann so aus:
1
// menus
2
3
struct UImenu menuMain =
4
{
5
  "Main Menu",       // menu name
6
  100,         // menu id
7
  5,         // number of entries
8
  {
9
    {"set OCR2", 1000},   // entries
10
    {"set preOn", 1001}, 
11
    {"set On", 1002},
12
    {"set postOn", 1003}, 
13
    {"submenu", 101}, 
14
  }
15
};
16
17
18
struct UImenu menuSub1 =
19
{
20
  "Sub Menu", 
21
  101, 
22
  2, 
23
  {
24
    {"Test 4", 1010}, 
25
    {"back", 100}
26
  }
27
};
28
29
struct UImenu *menuList[]={&menuMain, &menuSub1, NULL}; 
30
31
32
[...]
33
34
  while(1)
35
  {
36
    // enter menu
37
    int ret=UImenuExec();
38
    
39
    switch(ret)
40
    {
41
      //
42
      //  Main Menu
43
      //
44
      
45
      case 1000:  UIchgInt("set OCR2", setOCR2, getOCR2);
46
          break;  
47
          
48
      case 1001:  
49
          UIchgIntPtr("set preOn", &delayPreOn);
50
          break;
51
          
52
      case 1002:  
53
          UIchgIntPtr("set On", &delayOn);
54
          break;
55
          
56
      case 1003:  
57
          UIchgIntPtr("set postOn", &delayPostOn);
58
          break;
59
60
      // 
61
      // Submenu 1
62
      //
63
      
64
      case 1010:
65
          // tuwas
66
          break;
67
      
68
    }    
69
  }    
70
}

UImenuExec zeigt das menu an und gibt eine dem ausgewählten Eintrag 
entspr. ID zurück (Eintrag anwählen und irgend'n festgelegten button 
drücken).
UIchgIntPtr ist eine Subroutine in der man Werte eingeben kann (mit 4 
Tasten).

In 64k IDs ist auch genug Platz die Menu IDs unterzubringen, so 
funktionieren auch submenus ohne das UImenuExec was zurückgibt.

von Thilo M. (Gast)


Lesenswert?

Ich habe mich grade auch mit einem Menü 'rumgeschlagen.
Ich rufe zunächst ein Hauptmenü (gesonderte Endlosschleife) auf, in dem 
kann über swich/case ein Untermenü von 16 gewählt werden (einzelne 
Funktionen mit enthaltener Endlosschleife).
Durch die Displayausgaben, EEPROM lesen/Beschreiben, Werte verändern und 
begrenzen usw. frisst das Ding ordentlich Speicher. In meinem Regler 
(mit Sonderfunktionen) frisst das Menü beim mega644 schon 15% des Flash.
OK, da kann ich mit leben, aber Optimierung tut da auch Not, werde mich 
nach Abschluss des Projektes mal dranmachen und in die Codesammlung 
setzen.

von I_ H. (i_h)


Lesenswert?

Bei meiner Variante ist die Auswertung recht einfach, ich weis ehrlich 
gesagt nicht wie man da ~10kB Flash verheizen kann.

UImenuExec muss nur folgendes machen:
- menu ID von aktuell ausgewähltem menu und Eintragsnummer (pos. in 
array) in statischer variable speichern

- menuList nach passender menu ID für aktuelles Menu durchsuchen (1 
Schleife)
- Funktion aufrufen die dieses Menu mit aktuell gewähltem Eintrag 
malt/bei oben/unten Taste die Menueinträge durchgeht, bei ok Nummer des 
gewählten Eintrags zurückgibt
- Gucken ob Menu mit der ID des gewählten Eintrags vorhanden - wenn ja, 
als neue menu ID benutzen und von vorn anfangen, ansonsten Eintrags-ID 
zurückgeben

Und das war's eigentlich schon.

von Thilo M. (Gast)


Lesenswert?

>ich weis ehrlich gesagt nicht wie man da ~10kB Flash verheizen kann.

Mit der Menüstruktur selbst nicht. Das braucht auch fast nix.
Ich will aber in den einzelnen Untermenüs auch was tun. Bei vielen 
Untermenüs (bei mir 16) kommt da schon Code zusammen.
Z.B. einen Bildschirm anzeigen (immer Anders), eine Variable ausgeben, 
diese durch Tastendruck hoch, bzw: 'runterzählen, die Variable entweder 
im EEPROM abspeichern oder direktes Verlassen des Untermenüs. Die 
Tastenabfragen und Absicherungen gegen Überlauf usw. sind in vielen 
Untermenüs verschieden, darum kann hier nix zusammengefasst werden.
Das ist wie beim Windoof auch, sobald Benutzereingaben gemacht werden 
können müssen Gürtel und Hosenträger her. :)
Das kostet schon Platz. ;)

von Ralf (Gast)


Lesenswert?

@ I_ H.
Ich versuche gerade deine Menüstruktur nachzubauen.
Kannst du auch noch deine "UImenuExec" Funktion posten.
Viele Dank!

von I_ H. (i_h)


Lesenswert?

1
// current menu and menu list
2
int UImid;
3
struct UImenu **UImenuList;
4
5
// menu mode - 0 -> 2line display, 1 -> 1line display (lower)
6
char UImode;
7
8
// last id and mid, used to point to same 
9
// entry on menu recall
10
int UImidLast=-1;
11
int UIidLast;
12
13
14
15
// menu key masks
16
// #define KEY_UP    0x10
17
// #define KEY_DOWN  0x20
18
// #define KEY_SELECT  0x02 
19
20
#define MENU_LINE1 2
21
#define MENU_LINE2 3
22
23
24
25
26
// show menu entry
27
// l -> display line to use
28
// n -> entry number+1; note: 0 means show menu itself
29
// s -> if(s) show entry as selected
30
void UIshowEntry(struct UImenu *menu, char line, char n, char s)
31
{
32
  char baseLine, i;
33
  
34
  // menu sizes
35
  const int menuWidth=16;
36
  
37
  // selected entry
38
  if(line==0)  baseLine=MENU_LINE1;
39
  else    baseLine=MENU_LINE2;
40
  
41
  // clear display area and goto start
42
//   LCDgoto(base);
43
  LCDgotoXY(0, baseLine);
44
  
45
  for(i=0;i<menuWidth;i++) LCDsendChar(' ');
46
  
47
//   LCDgoto(base);    
48
  LCDgotoXY(0, baseLine);  
49
  
50
  
51
  // show entry
52
  if(n!=0)
53
  {
54
    
55
    if(s)  LCDsendString(">");
56
    else  LCDsendString(" ");
57
    
58
    LCDgotoXY(1, baseLine);
59
    LCDsendString(menu->e[n-1].name);
60
    if(UIsearchMid(menu->e[n-1].id)!=-1)  LCDsendChar('z'+4);
61
    
62
//     LCDgoto(base+menuWidth-1);        
63
    LCDgotoXY(menuWidth-1, baseLine);
64
    if(s)  LCDsendString("<");
65
    else  LCDsendString(" ");
66
  }
67
  
68
  // show menu
69
  else
70
  {
71
    if(s)  LCDsendString(">= ");
72
    else  LCDsendString(" = ");
73
    
74
    LCDgotoXY(3, baseLine);    
75
    LCDsendString(menu->menuName);
76
    
77
//     LCDgoto(base+menuWidth-3);        
78
     LCDgotoXY(menuWidth-3, baseLine);        
79
    if(s)  LCDsendString(" =<");
80
    else  LCDsendString(" = ");
81
  }
82
  
83
  return;
84
}
85
86
// select menu item
87
// return number of selected entry
88
// n is preselected entry
89
int UImenuSel(struct UImenu *menu, char n)
90
{
91
  char chg=1;  // anything has changed?
92
  
93
  // if 2line, select 1st entry per default
94
  // else, select menu as default
95
  signed char sel;
96
  
97
//   if(UImode==0)  sel=n; //+1;  
98
//   else    sel=n;
99
  sel=n+1;
100
  
101
  while(1)
102
  {
103
    // if anything changed
104
    if(chg)
105
    {
106
      // 2line display
107
      if(UImode==0)
108
      {
109
        // unselected entry
110
        signed char usel=sel-1;
111
        if(usel<0)  usel=menu->entries;
112
        
113
        UIshowEntry(menu, 0, usel, 0);
114
      
115
        // selected entry
116
        UIshowEntry(menu, 1, sel, 1);
117
      }
118
      
119
      // 1line display
120
      if(UImode==1)
121
      {
122
        // show selected entry
123
        UIshowEntry(menu, 1, sel, 1);
124
        
125
        // show entry number far right
126
        LCDgoto(0x40+20-2);
127
        LCDsendInt(sel);
128
      }
129
      
130
      chg=0;
131
    }
132
    
133
    // on key UP
134
    if(INPgetReset(KEY_UP))  
135
    {
136
      sel--;
137
      if(sel<0)  sel=menu->entries;
138
      
139
      chg=1;
140
    }
141
    
142
    // on key DOWN
143
    if(INPgetReset(KEY_DOWN))  
144
    {
145
      sel++;
146
      if(sel>menu->entries)   sel=0;
147
      
148
      chg=1;
149
    }
150
    
151
    // on key SELECT
152
    if(INPgetReset(KEY_OK))  
153
    {
154
      if(sel!=0)  return sel-1;
155
    }
156
    
157
    EventTick();
158
  }
159
}
160
161
// search if menu with given mid exists
162
// if so, return number of entry in UImenuList
163
// else, return -1
164
int UIsearchMid(int mid)
165
{
166
  int n=0;
167
  
168
  while(1)
169
  {
170
    if(UImenuList[n]==NULL) return -1;
171
    if(UImenuList[n]->mid==mid) return n;
172
    
173
    n++;
174
  }
175
  
176
  // dummy
177
  return 0;
178
}
179
180
// menu exec function
181
int UImenuExec()
182
{
183
  while(1)
184
  {
185
    // search menu number of mid
186
    int n=UIsearchMid(UImid);
187
    
188
    // sth failed
189
    if(n==-1) return -1;
190
    
191
    // show menu and let user select entry
192
    int ret;
193
    
194
    if(UImidLast==UImid)  ret=UImenuSel(UImenuList[n], UIidLast);
195
    else      
196
    {
197
      UImidLast=-1;
198
      ret=UImenuSel(UImenuList[n], 0);
199
    }
200
    
201
    // get id of selected entry
202
    int id=UImenuList[n]->e[ret].id;
203
    
204
    // if there's a menu with this id as mid, goto submenu
205
    if(UIsearchMid(id)!=-1)
206
    {
207
      UImid=id;
208
    }
209
    // else return id 
210
    else
211
    {
212
      UImidLast=UImid;
213
      UIidLast=ret;
214
    
215
      return id;
216
    }
217
    
218
    // event tick
219
    EventTick();
220
  }
221
  
222
  return 0;
223
}

Nicht besonders sauber programmiert, aber es funktioniert fehlerfrei 
(solange die menustruktur in ordnung ist).

Das EventTick() kann weg (gehört zu einem einfachen Multimthreading wo 
Tasks einstellbar alle xyz ms aufgerufen werden, daher in jeder 
while-schleife), irgendwo in dem Code ist auch noch die Möglichkeit das 
Menu 1- oder 2-zeilig anzeigen zu lassen (UImode).

Die Ausgabe ist in etwa so:
1
>== Menu Name ==<   -- selektierter Eintrag, daher die > <
2
  Item 1 
3
  Item 2
4
  .
5
  .
6
  .

Verweist die ID von Item xyz auf ein Submenu wird noch "->" angehängt 
(ist ein Zeichen, das 'z'+4).
INPgetReset gehört zum Key-Handler, gibt für die gegebene Key-Mask 
ungleich 0 zurück falls die Taste gedrückt wurde, und setzt den Status 
der Taste zurück falls dem so ist.

von I_ H. (i_h)


Lesenswert?

Ach ja, wär sicher net falsch das mal so umzuändern, dass die 
Strukturdaten im Flash liegen.

von Bruno (Gast)


Lesenswert?

Kannst dir dazu auch mal diesen Thread angucken:

Beitrag "strlen bei mehrdimensionalen Strings"

von I_ H. (i_h)


Lesenswert?

Meinst du weil die Zeilen vorher immer geleert werden? Wie gesagt, als 
ich den Code geschrieben habe wollte ich eigentlich was ganz anderes 
machen, brauchte dazu aber ein menu.

Außerdem spar ich so die lib-funktion ;)

Richtig Sinn macht das Menu übrigens erst mit dem Event-gedödel. Das 
besteht aus einem Array mit einem Eintrag/Task, da kann man einen 
Funktionszeiger reinschreiben und das Ausführungsintervall angeben (zB. 
alle 100msec).
Über einen Timer wird dann ermittelt welche Funktionen ausgeführt werden 
müssen, und das wird dann bei EventTick gemacht.

So kann man nebenher zB. ein paar Werte ausgeben, und muss sich dann 
garnimmer drum kümmern.

von Simon K. (simon) Benutzerseite


Lesenswert?

I_ H. wrote:
> Ach ja, wär sicher net falsch das mal so umzuändern, dass die
> Strukturdaten im Flash liegen.

Jep, besonders deine strings. Ansonsten: Die Idee sieht sehr sauber aus 
muss ich sagen.

von I_ H. (i_h)


Lesenswert?

In den prinzipiellen Aufbau ist auch verhältnismäßig die meiste Zeit 
geflossen... den Rest kann man ja bei Bedarf auch neu implementieren ;). 
Frei nach dem Motto Klassen sind dafür da dem ganzen Müll ein sauberes 
Interface zu verpassen (in dem Fall structs und funktionen).

So schaut dann übrigens ein etwas komplexeres Menu aus:
1
// menu structure
2
struct UImenu menuAkku =
3
{
4
  "Akku", 
5
  200, 
6
  4, 
7
  {
8
    {"Info", 210}, 
9
    {"Diag", 230},
10
    {"Prog", 220},
11
    {"leave", 2000}
12
  }
13
};
14
15
struct UImenu menuAkkuInfo = 
16
{
17
  "Info", 
18
  210, 
19
  5, 
20
  {
21
    {"sh. state", 2100}, 
22
    {"sh. energy", 2101}, 
23
    {"sh. all", 2102},
24
    {"hide", 2103},
25
    {"back", 200}
26
  }  
27
};
28
29
struct UImenu menuAkkuDiag =
30
{
31
  "Diag", 
32
  230, 
33
  4, 
34
  {
35
    {"set charge", 2300}, 
36
    {"set disch.", 2301}, 
37
    {"reset en.", 2302}, 
38
    {"back", 200}
39
  }
40
};
41
42
struct UImenu menuAkkuProg =
43
{
44
  "Prog",
45
  220, 
46
  6, 
47
  {
48
    {"Setup", 240}, 
49
    {"do disch.", 2200}, 
50
    {"do deep d.", 2201},
51
    {"do charge", 2202},
52
    {"stop", 2203}, 
53
    {"back", 200}
54
  }
55
};
56
57
struct UImenu menuAkkuProgSet =
58
{
59
  "Setup", 
60
  240, 
61
  8, 
62
  {
63
    {"charge", 2400}, 
64
    {"d.charge", 2406},
65
    {"cur. cur.", 2401},
66
    {"volt thres.", 2402},
67
    {"ch. time", 2403},
68
    {"d.ch. time", 2404},
69
    {"void time", 2405}, 
70
    {"back", 220}
71
  }
72
};

Es gibt noch ein 2. Menu im Zahlenraum 100, den Zeiger auf das Struct 
mit den Menus (UImenuList) kann man ja umladen. Man könnte jetzt auch 
noch für jeden Eintrag ein char mit Attributen dazunehmen, mir ist nur 
bisher noch nix sinnvolles eingefallen.

von Johann K. (Firma: privat) (johnboyk)


Lesenswert?

Sorry, wenn ich den 15 Jahre alten Thread nochmals bemühe, aber 
offensichtlich hat sich in der zugelassenen C-Syntax etwas geändert:
Ich versuche das Beispiel aus diesem ForumsArtikel nachzubauen, aber
folgender Code lässt sich einfach nicht (mehr) compilieren:
1
 
2
//     structure for one single menu entry
3
struct UImenuEntry
4
{
5
  const char* name;  // entry name
6
  int id;      // entry ID
7
};
8
9
//      structure for a simple menu 
10
struct UImenu
11
{
12
  const char* menuName;   // menu name
13
  int mid;                // menu ID  will be used to search for this entry
14
  
15
  char   entries;         // how many entries will be in the following list
16
  struct UImenuEntry e[]; // for list of menu-entries (see struct above)  
17
}; 
18
19
/* .......... */
20
21
//  definition of main menu
22
struct UImenu menuMain = {
23
  "Main Menu",              // menu name
24
  100,                      // menu id of this menu
25
  5,                        // number of entries in this menu
26
  {
27
    {"Main_01", 1000},      // entry with returncode when selected
28
    {"Main_02", 1001},      // entry with returncode when selected
29
    {"Main_03", 1002},      // entry with returncode when selected
30
    {"Main_04", 1003},      // entry with returncode when selected
31
    {"submenu", 101},       // entry with ID-Code of the submenu
32
  }
33
};

Fehlermeldung:
1
[Error] too many initializers for 'UImenuEntry [0]'

Hab natürlich mal nachgegoogelt und es hängt wahrscheinlich damit 
zusammen, dass man keine Inline-Initialization für listen-members mit 
unbekannter Listengröße mehr machen darf.
So ganz hab ich es nicht verstanden, aber wenn man auf Stackoverflow 
nach "too many initializers for 'int [0]' c++" sucht, findet man einen 
entsprechenden Hinweis...
Leider werde ich daraus nicht schlau, wie ich nun die Menüs definieren 
kann, damit sie der Compiler vernünftig schluckt....

Kann mir ein C-Guru von Euch hier bitte den richtigen Weg weisen...

Vielen Dank im Voraus
Johann

von Spess53 .. (hardygroeger)


Lesenswert?

Hi

C geht mir an Allerwertesten vorbei. Aber ich würde ein Menü als 
verkette (oder doppelt verkette) Liste aufbauen. Das Prinzip gibt es 
eigentlich in allen Programmiersprachen. Setzt aber die entsprechenden 
Sprachkenntnisse voraus-

MfG Spess

von Gerhard O. (gerhard_)


Lesenswert?

Spess53 .. schrieb:
> Hi
>
> C geht mir an Allerwertesten vorbei. Aber ich würde ein Menü als
> verkette (oder doppelt verkette) Liste aufbauen. Das Prinzip gibt es
> eigentlich in allen Programmiersprachen. Setzt aber die entsprechenden
> Sprachkenntnisse voraus-
>
> MfG Spess

spess53 bezieht sich auf das "linked lists" Konzept:

Guck mal hier; da gibt es einen Tutorial:

https://www.simplilearn.com/tutorials/data-structure-tutorial/linked-list-in-data-structure#:~:text=A%20linked%20list%20is%20the,reference%20to%20the%20next%20node.

https://www.geeksforgeeks.org/data-structures/linked-list/
https://www.geeksforgeeks.org/menu-driven-program-for-all-operations-on-singly-linked-list-in-c/
https://www.geeksforgeeks.org/complete-guide-to-dsa-for-beginners/?ref=shm


Würde ich auch so machen.

: Bearbeitet durch User
von Joe G. (feinmechaniker) Benutzerseite


Lesenswert?

Die sicherste Form einer Menüstruktur ist aus meiner Sicht immer eine 
Finite State Machines oder zu deutsch Automatengraph. Dieser kann vor 
der Realisierung auf mathematisch Vollständigkeit und 
Widerspruchsfreiheit geprüft werden. Hier mal ein kleines Beispiel eines 
einfachen Menüs [1].

[1] 
https://github.com/Feinmechaniker/micropython/blob/main/projects/FDevT/00%20docs/statemaschine_process.pdf

von J. S. (jojos)


Lesenswert?

Beim letzten submenu Entry ist ein Komma Zuviel am Ende, Kopierfehler.

von Johann K. (Firma: privat) (johnboyk)


Lesenswert?

Hallo!
Vielen Dank für die Hinweise auf die linked List bzw. State Machine.

Keine Sorge, von meinen Programmier-Skills her kann ich sowohl linked 
lists als auch state machines in Programmen umsetzen. Die Links auf 
geeksforgeeks.org finde ich trotzdem interessant.

Mir hat jedoch der Lösungsansatz in diesem Forums-Artikel gefallen, dass 
ich lediglich durch Inline-Definition der Menü-Einträge sowohl Menüs, 
als auch sub-Menüs einhängen kann und die gesamte Menü-Abarbeitung 
extrem schlank aufgebaut ist.
Wenn ich es als linked list löse, müsste ich meiner Meinung nach 
entweder pro SubMenü eine neue linked list definieren, oder mittels 
Menue-ID's und Filterung die Submenüs in einer Gesamt-linked-list 
abarbeiten.

Werde noch versuchen, das Inline-Initialisierung-Problem zu lösen und 
wenn ich das nicht schaffe, werde mal mit einem Lösungsansatz 
Gesamt-Linked-List beginnen.

Vielen Dank!

von J. S. (jojos)


Lesenswert?

Es lässt sich aber auch mit dem Komma kompilieren, jedenfalls mit gcc.

per cut&paste in https://www.onlinegdb.com/ kopiert, läuft.

Anstatt des langen switch/case blocks zur Auswertung würde ich function 
pointer in die UImenuEntry Strukturen packen.

: Bearbeitet durch User
von Random .. (thorstendb) Benutzerseite


Lesenswert?

Würde das mittels RTOS Thread in einer Klasse mit Thread-member (mind. 
in einem C Thread, wenn ohne Klassen) abbilden. Diese versendet Messages 
für Aktionen, und merkt sich den aktuellen Stand (bzw. geht einfach in 
ein osThreadFlagsWait(), vom Eingabethread aus getriggert).
In C++ lassen sich komplexere Menues gut als abgeleitete Klassen eines 
Basisobjektes erzeugen, von denen jede Klasse weiß, was sie kann. 
Verwaltung per Polymorphie.
Vielleicht ein bischen überzogen, dafür lesbar und einfach erweiterbar.
Und das RTOS bewahrt vor polling.

: Bearbeitet durch User
von Bruno V. (bruno_v)


Lesenswert?

Johann K. schrieb:
> Werde noch versuchen, das Inline-Initialisierung-Problem zu lösen und
> wenn ich das nicht schaffe,

Prinzipiell geht das wie in Deinem Beispiel, ist aber fehleranfällig 
weil n manuell bachgepflegt werden muss.

Nimm einfach Array und struzktur getrennt:

Ein Array unbekannter Größe mit Einträgen, und die Verwaltungsstruktur 
mit Name, ID, diesem Array und n als countof(dieses Array).

Einmal so angelegt, kannst Du x Einträge hinzufügen oder per #define 
rausnehmen ohne n nachzuführen

von Johann K. (Firma: privat) (johnboyk)


Lesenswert?

J. S. schrieb:
> Es lässt sich aber auch mit dem Komma kompilieren, jedenfalls mit gcc.
>
> per cut&paste in https://www.onlinegdb.com/ kopiert, läuft.
>

Ja, diese Inline-Initialisierung eines Pointer-Arrays unbestimmter Größe 
ist bei manchen Compilern noch akzeptiert, aber dürfte nicht exakter 
Standard sein, weshalb manche Compiler dies nicht akzeptieren...

> Anstatt des langen switch/case blocks zur Auswertung würde ich function
> pointer in die UImenuEntry Strukturen packen.

Ja, es gibt noch einiges Verbesserungspotential wie function-pointer und 
Strings in den Progmem und non-blocking für den Microprozessor-loop().

Zuerst wollte ich aber einmal den Beispielcode zum laufen bringen und 
dann entsprechend umbauen...
Mit C komme ich ganz gut zu recht inklusive einfachen Pointern. Aber bei 
Pointer-Listen bzw. Arrays von pointern hab ich noch ein bissl an der 
Syntax zu knabbern.
Was das ist und wie es intern arbeiten soll, verstehe ich, weil ich 
schon vor 30 Jahren in Assembler auf Großrechnern programmiert habe. 
Aber in C die richtige Syntax zusammenzubringen ist für mich in manchen 
Fällen wie diesem eine Herausforderung...

Danke für Eure Ratschläge und Unterstützung!

von J. S. (jojos)


Lesenswert?

es läuft in C und C++, auch mit den moderneren C++ Varianten. Nur das 
alte Turbo C/C++ liefert den Fehler (im OnlineGDB ausgeführt).

Die Funktionszeiger kann man mit typedef entschärfen, hier auch ohne 
Anzahl und mit Endekennung:
1
#include <stdio.h>
2
3
//     structure for one single menu entry
4
5
typedef void (*UIMenuFn)();
6
7
typedef struct 
8
{
9
  const char*   name;       // entry name
10
  UIMenuFn      fn;         // entry function
11
} UImenuEntry;
12
13
//      structure for a simple menu 
14
15
typedef struct 
16
{
17
  const char* menuName;     // menu name
18
  UImenuEntry e[];          // for list of menu-entries (see struct above)  
19
} UImenu; 
20
21
/* .......... */
22
23
//  definition of main menu
24
25
void menuFn1() {
26
    printf("function 1\n");
27
}
28
29
void menuFn2() {
30
    printf("function 2\n");
31
}
32
33
UImenu menuMain = {
34
  "Main Menu",              // menu name
35
  {
36
    {"Main_01", menuFn1},      // entry with returncode when selected
37
    {"Main_02", menuFn1},      // entry with returncode when selected
38
    {"Main_03", menuFn2},      // entry with returncode when selected
39
    {"Main_04", menuFn2},      // entry with returncode when selected
40
    {"submenu", menuFn2},       // entry with ID-Code of the submenu
41
    {NULL, 0}
42
  }
43
};
44
45
46
int main()
47
{
48
    int i = 0;
49
50
    printf("menu: %s\n", menuMain.menuName);
51
    
52
    while(menuMain.e[i].name) {
53
        printf("  %s: ", menuMain.e[i].name);
54
        
55
        if (menuMain.e[i].fn) {
56
            menuMain.e[i].fn();
57
        }
58
        
59
        i++;
60
    }
61
62
    return 0;
63
}

: Bearbeitet durch User
von Martin S. (mmaddin)


Lesenswert?

Stefan S. schrieb:
> nur über eine if Anweisung

Schau dir mal die "Kontrollstrukturen" in C an.

Stefan S. schrieb:
> weiß jemand von Euch einen besseren Rat?

Da es sehr viele Möglichkeiten gibt und nichts von deinem Umfeld bekannt 
ist, wäre es besser etwas konkreter zu werden.

-Wie liegen die Tastinformetionen vor?
-Wofür sollen die Tasten im Menü benutzt werden
-Wie sieht die Menüstruktur genau aus
-Geht es darum Texte zu wechseln oder unterschiedliche Werte zu 
"stellen" oder zu setzen.
-Wie sind die Datenstrukturen der Werte oder Texte die "bedient" werden 
sollen
-Wie ist die Programmstruktur?...


Also was genau ist der Umfang und worauf liegt das Hauptaugenmerk usw...

von Ali K. (teddy50)


Lesenswert?

Ich sage nur Strategy Pattern dazu.

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.