Zustandsautomat mit avr-gcc

Persönliche Seite #1304458
Lesenswert?

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
Gast #1304493
Lesenswert?

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
case STATE_ABC: state_abc(); break;
4
case STATE_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.
Gast #1305085
Lesenswert?

>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
#1305104
Lesenswert?

... schrieb:
>
1
> switch (state)
2
> {
3
> case STATE_ABC: state_abc(); break;
4
> case STATE_DEF: state_def(); break;
5
> ...
6
> }
7
>

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();
Gast #1305200
Lesenswert?

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?
Persönliche Seite #1305325
Lesenswert?

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
typedef enum
3
{
4
    STATE0,
5
    STATE1,
6
    
7
    STATE_NUM
8
}  state_t;
9

10
typedef struct
11
{
12
   int wetter;
13
   int boerse;
14
} welt_t;
15

16
typedef state_t (*statehandler_t)(welt_t*);
17

18
extern state_t handler0 (welt_t*);
19
extern state_t handler1 (welt_t*);
20

21
// <-- header end -->
22
statehandler_t handler[] = 
23
{
24
    handler0,
25
    handler1
26
};
27

28
static welt_t welt = 
29
{
30
   .wetter = 42,
31
   .boese  = -1
32
};
33

34
void main_loop (void)
35
{
36
    state_t state = 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
extern state_t handler0 (welt*);
2
extern state_t handler1 (welt*);
3

4
void main_loop (void)
5
{
6
    state_t state = handler0;
7
    
8
    while (1)
9
    {
10
        state = state (&welt);
11
    }
12
}

Preisfrage:
  Wie sieht der Typedef für state_t aus?

Johann
Gast #1305412
Lesenswert?

> ...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
typedef void (*statehandler_t)(welt_t*);
2

3
typedef struct
4
{
5
   int wetter;
6
   int boerse;
7
   statehandler_t state;
8
} welt_t;
9

10
extern void handler0 (welt_t*);
11
extern void handler1 (welt_t*);
12

13
static welt_t welt = 
14
{
15
   .wetter = 42,
16
   .boese  = -1,
17
   .state = handler0
18
};
19

20
void main_loop (void)
21
{
22
    while (1)
23
    {
24
        welt.state(&welt);
25
    }
26
}
Persönliche Seite #1305445
Lesenswert?

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
> typedef struct
2
> {
3
>    int wetter;
4
>    int boerse;
5
>    statehandler_t state;
6
> } welt_t;
7
> 
8
> void main_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:
1
struct welt_t;
2

3
typedef struct state_t
4
{
5
    struct state_t * (*(*state) (struct welt_t*))(struct welt_t*);
6
} state_t;
7

8
extern state_t * (*slartibartfas (struct welt_t * welt))(struct welt_t*);
9

10
extern struct welt_t * get_welt (void);
11

12
void main_loop (void)
13
{
14
    struct welt_t * welt = get_welt();
15
    state_t * (*state)(struct welt_t*) = slartibartfas (welt);
16
    
17
    while (1)
18
        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:
1
main_loop:
2
  push r28   ; 
3
  push r29   ; 
4
/* prologue: function */
5
/* frame size = 0 */
6
  call get_welt   ; 
7
  movw r28,r24   ;  welt,
8
  call slartibartfas   ; 
9
.L4:
10
  movw r30,r24   ;  state,
11
  movw r24,r28   ; , welt
12
  icall

Johann
#1305451
Lesenswert?

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.
Persönliche Seite #1305460
Lesenswert?

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.
1
state_t * (*slartibartfas (struct welt_t * welt))(struct welt_t*)
2
{
3
    (void) welt;
4
    return slartibartfas;
5
}

Im Endeffekt passieren an den Stellen, wo gewarnt wird, Pointer-Casts, 
die man ja nicht haben will (zumindest wenn man sauber programmiert).

Johann
Gast #1305600
Lesenswert?

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
Gast #1305699
Lesenswert?

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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren