Weder im Wiki-Artikel noch in den gefundenen Threads habe ich eine
Lösung gesehen, die eine tabellarische FSM mit mehreren Events pro
Zustand abbildet.
Zwei Ansätze fallen mir ein:
Eine Event-Array bzw. ein Pointer dorthin (was es unübersichtlicher
macht, da separat definiert)
STATEx, [EVENTy->[FUNCTION,NEWSTATE], ...]
...
Entsprechend viele Einträge und eine Suche (größere Tabelle, langsamer,
aber schön übersichtlich):
STATE1, EVENT_A, FUNCTION, NEWSTATE
STATE1, EVENT_B, FUNCTION, NEWSTATE
...
Ist der Ansatz brauchbar, wie geht's "besser"?
Sehr sauber lässt sich so etwas mit dem State Pattern lösen. Ist OOP und
damit besser in C++ umzusetzen, aber wo C läuft läuft fast immer auch
C++, und es vermeidet so ein Schlachtfeld an Makros. Es ist sehr
flexibel und bietet die Möglichkeit, pro Zustand Daten abzulegen die
auch nur für diesen Zustand gültig sind, beliebige individuelle
Übergangsfunktionen oder -Bedingungen und nebenher ist es auch noch 100%
typsicher ohne Casts umsetzbar.
Das sieht gut aus (entspricht vermutlich meiner ersten Variante), aber
für mich ist es leider nicht offensichtlich, wie die Makros (und Daten)
dahinter aussehen.
Als Ergänzung zu meiner ersten Idee könnte man natürlich auch einfach
eine Tabelle entsprechend der maximalen Anzahl von Events anlegen, und
die "Platzverschwendung" in Kauf nehmen.
Flederdil schrieb:> Als Ergänzung zu meiner ersten Idee könnte man natürlich auch einfach> eine Tabelle entsprechend der maximalen Anzahl von Events anlegen, und> die "Platzverschwendung" in Kauf nehmen.
das würde ich nicht tun, da du wirklich zu viel Platz verschenkst
Stephan schrieb:> das würde ich nicht tun, da du wirklich zu viel Platz verschenkst
Ich auch nicht - verrate doch bitte, wie die Makros funktionieren ;-)
Flederdil schrieb:> Das sieht gut aus (entspricht vermutlich meiner ersten Variante), aber> für mich ist es leider nicht offensichtlich, wie die Makros (und Daten)> dahinter aussehen.
ich habe mir 3 Strukturen dafür aufgebaut.
1
// der Container für die Events
2
structeventTransTab_s{
3
void(*transFunc)(event_t*conste);/* transition function */
/* wir haben zwar den State gefunden aber kein passendes Event! */
37
break;
38
}
39
}
40
41
returnFalse;
42
}
so das war es. Ich hoffe ich habe nichts Kaput gemacht oder vergessen zu
kopieren. :-)
Es ist ein Ansatz, ob er dir gefällt und du diese Art der Programmierung
mit den Macros mags, musst du entscheiden.
PS: Du kannst auch diese FSM-Macros umschreiben und mit Hilfe von
Plantuml ein Diagramm zeichnen lassen. Es sieht nicht schön aus, bei
mir, aber es hilft beim Programmieren.
Stephan schrieb:> if ( eventTransition->transFunc != NULL_PTR
Und warum nicht einfach NULL? Es gibt keine sinnvolle Situation in C in
welcher man den Null-Pointer anders definieren würde als NULL, solche
Konstrukte machen es nur schlechter lesbar & wartbar.
Programmierer schrieb:> Und warum nicht einfach NULL?
Warum überhaupt irgendeine Null zum Vergleich?
if ( eventTransition->transFunc )
sollte doch reichen.
Stephan schrieb:> Ich hoffe ich habe nichts Kaput gemacht oder vergessen zu> kopieren. :-)
Es fehlen noch ein paar Typen.. Habe versucht, zu raten, aber das
funktioniert noch nicht:
1
#include<stdint.h>
2
3
typedefenum{STATE_ZERO,STATE_ONE}state;
4
typedefenum{EV_ZERO,EV_ONE,EV_TWO}event_id_t;
5
typedefvoid*event_t;
6
typedefuint8_tsize_t;
7
8
// der Container für die Events
9
structeventTransTab_s{
10
void(*transFunc)(event_t*conste);/* transition function */
Flederdil schrieb:> Stephan schrieb:>> Ich hoffe ich habe nichts Kaput gemacht oder vergessen zu>> kopieren. :-)>> Es fehlen noch ein paar Typen.. Habe versucht, zu raten, aber das> funktioniert noch nicht:>
1
#include<stdint.h>
2
3
typedefenum{STATE_ZERO,STATE_ONE}state;
4
typedefenum{EV_ZERO,EV_ONE,EV_TWO}event_id_t;
5
typedefvoid*event_t;
6
typedefuint8_tsize_t;
> Sollte ".eventId" nicht eigentlich auch event_id_t sein?
Ja ist richtig. Das war mal ein Enum.
Programmierer schrieb:> Stephan schrieb:>> size_t i = (size_t)0>> Der Cast nach size_t beim Initialisieren ist unnötig.
Ich bekomme hier eine Warnung "Implizierter CAST". daher der cast von
meiner Seite. Ich glaube, ich hätte auch "0UL" schreiben können.
Programmierer schrieb:> Stephan schrieb:>> if ( eventTransition->transFunc != NULL_PTR>> Und warum nicht einfach NULL? Es gibt keine sinnvolle Situation in C in> welcher man den Null-Pointer anders definieren würde als NULL, solche> Konstrukte machen es nur schlechter lesbar & wartbar.
Bei meinem Compiler ist NULL wirklich eine dezimale 0!
Daher würde das eine Warnung vom Compiler generieren!
1
#define NULL_PTR ((void*)0)
Dies wird akzeptiert!
EAF schrieb:> Programmierer schrieb:>> Und warum nicht einfach NULL?> Warum überhaupt irgendeine Null zum Vergleich?>> if ( eventTransition->transFunc )> sollte doch reichen.
Die ist kein Boolesche Ausdruck und es gibt eine Warnung!
Mein Compiler hat ein paar MISRA Regeln mit eingebaut und prüft diese
auch.
Die "##" musste ich bei meinen Compiler auch erstmal freigeben, sind
auch nicht erlaubt!
Stephan schrieb:> Ich bekomme hier eine Warnung "Implizierter CAST".
Meines Wissens ist das eine Initialisierung und kein Cast, daher völlig
legal. Das geht mit allen Integer-Typen.
Stephan schrieb:> Bei meinem Compiler ist NULL wirklich eine dezimale 0!
Das ist auch legitim.
> Daher würde das eine Warnung vom Compiler generieren!
Nö, 0 ist implizit in jeden Pointer-Typ konvertierbar, daher kann man
alle Pointer-Typen mit 0 vergleichen oder zuweisen. In aktuellen
C++-Versionen ist es dank nullptr eleganter gelöst, aber in C geht es
halt so. NULL ist explizit genau dafür gedacht, auf Pointer zugewiesen
oder verglichen zu werden, daher macht es keinen Sinn etwas eigenes
dafür zu definieren. Wenn der Compiler davor warnt, etwas genau dafür zu
benutzen wofür es vorgesehen ist, stimmt mit dem was nicht.
Stephan schrieb:> Die ist kein Boolesche Ausdruck und es gibt eine Warnung!
Es ist nach bool konvertierbar, und daher ist es absolut korrekt und
wohldefiniert. Wer hier warnt, ist extrem paranoid, denn hier gibt es
kein Fehlerpotenzial.
Stephan schrieb:> Mein Compiler hat ein paar MISRA Regeln mit eingebaut und prüft diese> auch.
Ah, MISRA. Dann haben wir hier ein paar tolle Beispiele, wie MISRA den
Code schlechter macht - schlechte Lesbarkeit (NULL_PTR ???) kann eben
auch Sicherheitsprobleme bewirken, weil man nicht mehr sieht, was
passiert!
Stephan schrieb:> Die "##" musste ich bei meinen Compiler auch erstmal freigeben, sind> auch nicht erlaubt!
Ist auch absolut Standard und erlaubt. Sich so etwas erst zu verbieten
und dann wieder zu aktivieren...
Mit meinem obigen Versuch funktioniert es nicht, d.h. ich erhalte eine
Reihe an solchen Fehlern:
error: pasting "{" and "CREATE_STATE" does not give a valid
preprocessing token
Flederdil schrieb:> struct fsm test {
Hier fehlt wohl auch noch ein = ?
Und: gab es nicht einen Schalter für den Compiler (GCC), damit er den
output nach dem Präprozessor nicht löscht? Ich würde mir gerne das
Resultat ansehen...
Danke dir, aber mein GCC mag es (Anhang) leider immer noch nicht. Die
Fehler
error: pasting "{" and "ADD_EVENT" does not give a valid
preprocessing token
bleiben.
Vielleicht macht der Präprozessor nicht genügend Durchläufe?
Hallo,
so mit dem Code läuft es bei mir mit dieser Version:
gcc version 8.1.0 (x86_64-posix-seh-rev0, Built by MinGW-W64 project)
gcc -save-temps test.c -o test1 -Wall -std=c11 -D__USE_MINGW_ANSI_STDIO
Sorry, habe das File doppelt angehängt.
Hallo nochmal, ich habe mich wohl nicht klar ausgedrückt: es lief oben
auch schon bei mir! Der Abbruch bezog sich auf die Ausgabe des
"präprozessierten" Codes.
Nochmals besten Dank.