Hallo!
Wie realisiert man am besten einen Zustandsautomat mit dem GCC? Einfach
über eine Zustandsvariablen und switch() Anweisungen oder gibts da
bessere Wege, oder gar Compilerunterstützung?
MFG
Frank schrieb:
> Wie realisiert man am besten einen Zustandsautomat mit dem GCC? Einfach> über eine Zustandsvariablen und switch() Anweisungen oder gibts da> bessere Wege, oder gar Compilerunterstützung?
Es gibt Beschreibungssprachen für Zustandsautomaten (state machines)
und Compiler aus diesen Sprachen nach C, C++,...
http://freshmeat.net/search?q=state+machine&submit=Search
Vielleicht noch ein kleiner Tipp:
Bevor du jetzt zum softwaretechnischen Rundumschlag ausholst, denk bitte
daran:
KISS - keep it simple stupid
Das soll heißen, wenn du dein Problem einfach mit einer Zustandsvariable
und einem switch lösen kannst, dann mach es auch so. Du solltest aber
dann nicht die gesamte Funktionalität in der switch-Anweisung
ausprogrammieren. Also ich halte sowas hier für sinnvoll:
1
switch(state)
2
{
3
caseSTATE_ABC:state_abc();break;
4
caseSTATE_DEF:state_def();break;
5
...
6
}
Ich hab schon Sourcecode gesehen, da hat einer das gesamte Programm in
einen switch Block gepackt, der dann weit über 2000 Zeilen lang war. Da
sieht niemand mehr durch. Er hatte dann auch keine Lust gehabt, neue
Zustände einzuführen und hat dann die Funktion rekursiv mit
irgendwelchen Nebenbedingungen aufgerufen.
>Er hatte dann auch keine Lust gehabt, neue>Zustände einzuführen und hat dann die Funktion rekursiv mit>irgendwelchen Nebenbedingungen aufgerufen.
In diese Versuchung kam ich auch schon, erschien mir einfacher und
übersichtlicher eben weil kein zusätzlicher state.
Also werd ich nach dem KISS arbeiten ariable+switch
Naja, das ist doch für Anfänger ganz praktikabel. Aber als Steigerung
kannst Du es gleich so machen:
// Zustandvariable ist ein Funktionszeiger
void (*state)(void);
// Zustand einstellen
state = state_abc;
// Zustand ausführen:
state();
Zeiger auf Funktion kenn ich on Callbacks, aber welchen Vorteil hat das
hier? Man muss ja trotzdem den aktuellen State abfragen. Ob ich
verschiednene Funktionen aufrufe, oder der state() Funktion eine
Addresse zuweise und ausführe ändert ja nichts. Oder doch?
Skua schrieb:
> @Frank>> Abfrage erübrigt sich bei Funktionszeigern, die Funktion ist mit dem> Status identisch.
Kannst du das konkretisieren, zB in C?
Ich wüsste jetzt nicht, wie ich einen Funktionszeiger-Typ definieren
könnte für Funktionen, die Funktionen dieses Typs zurückliefern.
Konkret als Skelett mal folgender C-Code:
1
// <-- header start -->
2
typedefenum
3
{
4
STATE0,
5
STATE1,
6
7
STATE_NUM
8
}state_t;
9
10
typedefstruct
11
{
12
intwetter;
13
intboerse;
14
}welt_t;
15
16
typedefstate_t(*statehandler_t)(welt_t*);
17
18
externstate_thandler0(welt_t*);
19
externstate_thandler1(welt_t*);
20
21
// <-- header end -->
22
statehandler_thandler[]=
23
{
24
handler0,
25
handler1
26
};
27
28
staticwelt_twelt=
29
{
30
.wetter=42,
31
.boese=-1
32
};
33
34
voidmain_loop(void)
35
{
36
state_tstate=STATE0;
37
38
while(1)
39
{
40
state=handler[state](&welt);
41
}
42
}
Hier muss doch wieder eine Status-Variable eingeführt werden. Schaffst
du es ohne eine solche Variable? Wie sieht dann der Typedef für
statehandler_t aus, ohne daß man Schweinereien wie Pointer-Casts braucht
oder über globale Variablen (bäh) kommuniziert?
Du willst also sowas stattdessen:
1
externstate_thandler0(welt*);
2
externstate_thandler1(welt*);
3
4
voidmain_loop(void)
5
{
6
state_tstate=handler0;
7
8
while(1)
9
{
10
state=state(&welt);
11
}
12
}
Preisfrage:
Wie sieht der Typedef für state_t aus?
Johann
> ...ohne daß man Schweinereien wie Pointer-Casts braucht> oder über globale Variablen (bäh) kommuniziert?
Dein Code enthält doch auch eine globale Variable.
Aber wie wär's damit (vielleicht noch mit geschickterer Namensgebung):
Rolf Magnus schrieb:
>> ...ohne daß man Schweinereien wie Pointer-Casts braucht>> oder über globale Variablen (bäh) kommuniziert?>> Dein Code enthält doch auch eine globale Variable.> Aber wie wär's damit (vielleicht noch mit geschickterer Namensgebung):>>
1
>typedefstruct
2
>{
3
>intwetter;
4
>intboerse;
5
>statehandler_tstate;
6
>}welt_t;
7
>
8
>voidmain_loop(void)
9
>{
10
>while(1)
11
>{
12
>welt.state(&welt);
13
>}
14
>}
15
>
Den Status hatte ich ganz bewusst aus "welt" rausgehalten, weil er da
konzeptionell nicht hingehört. Die Statemaschine operiert auf einem
Objekt, sie ist nicht darin verwoben.
Mit folgendem Code kann man vielleicht sowas erreichen, aber wirklich
verständlich ist das auch nicht gerade und irgendwie grenzwertig:
state=state(welt);// warning: assignment from incomparible pointer type
19
}
Leider gibt es in der gekennzeichneten Zeile noch eine Warnung. Das
ganze ist eher ne Spielerei, um rauszufinden ob sowas geht in C. In
Haskell oder so ist das bestimmt easy...
Der Code ist aber immerhin korrekt trotz der Warnung:
Johann L. schrieb:
> typedef struct state_t> {> struct state_t * (*(*state) (struct welt_t*))(struct welt_t*);> } state_t;
Ui.
Das ist aber hart :-)
Ich habs auch noch nie geschafft. Ein typedef für einen
Funktionspointer, der einen Funktionspointer auf denselben Typ
zurückgibt, ist eines der grossen Mysterien in C.
PS: Ich würds gar nicht so kompliziert machen.
Einfach einen globalen Funktionspointer und jeder State hat das Recht
einen anderen State dort einzutragen.
Karl heinz Buchegger schrieb:
> Johann L. schrieb:>>> typedef struct state_t>> {>> struct state_t * (*(*state) (struct welt_t*))(struct welt_t*);>> } state_t;>> Ui.> Das ist aber hart :-)>> Ich habs auch noch nie geschafft. Ein typedef für einen> Funktionspointer, der einen Funktionspointer auf denselben Typ> zurückgibt, ist eines der grossen Mysterien in C.>> PS: Ich würds gar nicht so kompliziert machen.> Einfach einen globalen Funktionspointer und jeder State hat das Recht> einen anderen State dort einzutragen.
Ist wie gesagt um die Grenzen auszuloten und zu testen, ob sowas
überhaupt geht. In meinen Programmen würd ich sowas net hinschreiben.
Was noch stört ist die Struktur, aber wie man die wegbekommen kann, da
hab ich echt keine Idee. Und in Standard-C (also nicht in GNU-C) gibt
das bestimmt Fehler.
Man kann natürlich eine Funktion definieren, die als Rückgabewert (oder
auch als Partameter) Funktionszeiger hat, aber die sind dann von einem
anderen Typ.
Auch eine Implementierung von slartibartfas, die nichts am State ändert,
liefert eine Warning.
ja, wirklich beeindruckend wie man einen einfachen übersichtlichen
switch so programmieren kann, dass man gar nichts mehr versteht. Ich
hoffe, dass das beim philosophieren bleibt, sonst erschiene es mir etwas
masochistisch.
l.g. gebhard
> Den Status hatte ich ganz bewusst aus "welt" rausgehalten, weil er> da konzeptionell nicht hingehört.
Du wolltest ja keine separate globale Variable. Notfalls kann man auch
eine Struktur nochmal außenrum machen, so daß welt ein Teil davon ist
und der Status ein anderer.
> Die Statemaschine operiert auf einem Objekt, sie ist nicht darin> verwoben
Das Objekt hat aber einen Zustand. Deshalb sehe ich kein Problem darin,
diesen im Objekt zu speichern. Das hängt aber davon ab, ob die
Statemachine das Objekt zur Laufzeit wechseln soll und dabei aber ihren
Zustand beibehalten muß. In der Regel würde ich davon ausgehen, daß das
nicht der Fall ist.
Mir gefällt es irgendwie nicht, daß deine Zustandsfunktionen jeweils
einen neuen Zustand als Returnwert zurückgeben. Da finde ich das
Speichern im Objekt schöner.