Hallo C-Gemeinde, ich weiss einfach nicht wonach ich noch suchen soll.
Was mir einfiel, brachte mich nicht wirklich weiter...
Bin gerade dabei eine Menüstruktur für einen µC zu schreiben. Dabei
möchte ich gern das Prinzip der verketteten Liste nutzen. Nun habe ich
aber das Problem, das ich auf Strukturen zeigen muß die noch nicht
Initialisiert sind. Gibt es dafür in C eine Lösung? Ich könnte natürlich
alle Menüeinträge in eine Struktur schreiben, das Ganze wird aber bei
größeren Menütiefen extrem unübersichtlich. Außerdem muß man zum
Einfügen eines neuen Menüpunktes alle nachfolgenden Menü-Zeiger ändern,
was die Sache nicht unbedingt Fehlerunanfälliger macht.
1 | #define ANZAHL_TASTEN 4
| 2 |
| 3 | /*grundlegender datentyp */
| 4 | typedef struct m{ struct m *upmen; /* Menü eine Ebene höher, falls vorhanden */
| 5 | struct m *prev; /* Das vorhergehende Menü dieser Ebene */
| 6 | struct m *next; /* Das nächste Menü dieser Ebene */
| 7 | struct m *submen; /* Untermenü, falls vorhanden */
| 8 | char *mtxt; /* Menütext */
| 9 | void (*mfun)(struct m**); /* Menüfunktion, falls vorhanden */
| 10 | char *mdat; /* Menüdaten */
| 11 | /* Die diesem Menüpunkt zugeordneten Tastenfunktionen */
| 12 | void (*tastfun[ANZAHL_TASTEN])(struct m**);
| 13 | }MENU;
| 14 |
| 15 | // Submenü 1
| 16 | MENU sm1[]={ {&omu[0], &sm1[0], &sm1[1], NULL, "Menu 1.1", dumfun, NULL, {curup, curdwn, upmen, dumfun}},
| 17 | {&omu[0], &sm1[0], &sm1[2], NULL, "Menu 1.2", dumfun, NULL, {curup, curdwn, upmen, dumfun}},
| 18 | {&omu[0], &sm1[1], &sm1[3], NULL, "Menu 1.3", dumfun, NULL, {curup, curdwn, upmen, dumfun}},
| 19 | {&omu[0], &sm1[2], &sm1[3], NULL, "Menu 1.4", dumfun, NULL, {curup, curdwn, upmen, dumfun}}};
| 20 |
| 21 | // Oberste Menüebene
| 22 | MENU omu[]={ {NULL, &omu[0], &omu[1], sm1, "Menu 1 ", dumfun, NULL, {curup, curdwn, dumfun, submen}},
| 23 | {NULL, &omu[0], &omu[2], NULL, "Menu 2 ", dumfun, NULL, {curup, curdwn, dumfun, dumfun}},
| 24 | {NULL, &omu[1], &omu[3], NULL, "Menu 3 ", dumfun, NULL, {curup, curdwn, dumfun, dumfun}},
| 25 | {NULL, &omu[2], &omu[3], NULL, "Menu 4 ", dumfun, NULL, {curup, curdwn, dumfun, dumfun}}};
| 26 |
| 27 |
| 28 | /* Das vorhergehende Menü dieser Ebene */
| 29 | void curup(MENU **men)
| 30 | {
| 31 | MENU *m;
| 32 | m = (*men)->prev;
| 33 | if (m)
| 34 | *men = m;
| 35 | }
| 36 |
| 37 | /* Das nächste Menü dieser Ebene */
| 38 | void curdwn(MENU **men)
| 39 | {
| 40 | MENU *m;
| 41 | m = (*men)->next;
| 42 | if (m)
| 43 | *men = m;
| 44 | }
| 45 |
| 46 | /* Untermenü */
| 47 | void submen(MENU **men)
| 48 | {
| 49 | MENU *m;
| 50 | m = (*men)->submen;
| 51 | if (m)
| 52 | *men = m;
| 53 | }
| 54 |
| 55 | /* Menü eine Ebene höher */
| 56 | void upmen(MENU **men)
| 57 | {
| 58 | MENU *m;
| 59 | m = (*men)->upmen;
| 60 | if (m)
| 61 | *men = m;
| 62 | }
| 63 |
| 64 | /* Displaytext anzeigen */
| 65 | void display (MENU **men)
| 66 | {
| 67 | sendStringSPI((*men)->mtxt);
| 68 | }
| 69 |
| 70 |
| 71 | MENU *zeile;
| 72 |
| 73 | int main(void)
| 74 | {
| 75 | zeile = omu;
| 76 |
| 77 | while (1)
| 78 | {
| 79 | taste = get_key();
| 80 | if(taste)
| 81 | {
| 82 | zeile->tastfun[taste-1](&zeile);
| 83 | zeile->mfun(&zeile);
| 84 | display(&zeile);
| 85 | }
| 86 | }
| 87 | return 0;
| 88 | }
|
Mein Problem ist nun konkret, das ich im Array sm1[] auf om[] verweisen
möchte, welches aber zu diesem Zeitpunkt noch gar nicht existiert.
1 | MENU sm1[]={ {&omu[0],...
|
Weiß jemand Rat?
Ach ja, das Programm hier muß jetzt nicht 100%ig lauffähig sein. Habe
ich nur schnell aus meiner Anwendung zusammenkopiert. Es soll nur den
Sachverhalt verdeutlichen
Viele Grüße,
Steffen
Es sollte gehen, wenn Du ein
voranstellst. Eine Extern-Deklaration und eine Definition im selben
File beißen sich nicht.
Das Problem mit dem Hauptmenü habe ich in meinem Wettbewerbsbeitrag von
2008, Tinykon, dadurch gelöst, dass es kein Hauptmenü gibt, auf das zur
Compilierzeit verwiesen wird.
Die Datenstruktur von Tinykon ist der hier vorgestellten Menüstruktur
ähnlich (Menüpunkte in doppelt verketteter Liste, Tastatur im
Zeigerarray, usw), allerdings sind die Menüebenen streng hierarchisch
dem jeweils aufrufenden Menü untergeordnet, es gibt keinen Rückzeiger,
der aus dem Untermenü auf das aufrufende Menü zurückzeigt.
Da alle Menüeinträge vom gleichen Datentyp sind, gelangt man zum
gewünschten Untermenü durch rekusiven Aufrauf der (Menü)Startfunktion,
der der Zeiger auf das gewählte Untermenü übergeben wird. Zur
übergeordneten Menüebene gelangt man zurück, in dem man die
Startfunktion einfach verlässt. Der Compiler sorgt für den richtigen
Ablauf.
Natürlich funktioniert diese Methode nur, wenn der Zeiger auf das
übergeordnete Menü auf dem Stapel abgelegt wird, pro Menüebene ein
Zeiger.
mfg
> Hc Zimmerer
Danke für den Tipp, das ist des Rätsels Lösung! Funktioniert perfekt,
jetzt kann's ans Eingemachte gehen.
> G. O.
Tatsächlich entstammen 90% der Funktionalität des Menüs deinem Tinykon.
Dafür ein großes Dankeschön! Vor allem die Funktionszeiger sind genial
gelöst. Wenn man es erst mal begriffen hat, extrem Übersichtlich und
Wartungsfreundlich :-) Allerdings möchte ich in der Main() mehrere
Prozesse abarbeiten, also habe ich es an meine Bedürfnisse angepasst. Es
ist ja im Prinzip nur ein Zeiger mehr! Dadurch spart man sich aber die
Rekusivität. Die Menüfunktion wird nur bei Bedarf aufgerufen und kehrt
in die Main() zurück.
MfG
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|