Nous devons mettre en œuvre une machine simple état C .
Une instruction switch standard est-elle la meilleure façon de procéder?
Nous avons un état actuel (état) et un déclencheur pour la transition.
switch(state)
{
case STATE_1:
state = DoState1(transition);
break;
case STATE_2:
state = DoState2(transition);
break;
}
...
DoState2(int transition)
{
// Do State Work
...
if(transition == FROM_STATE_2) {
// New state when doing STATE 2 -> STATE 2
}
if(transition == FROM_STATE_1) {
// New State when moving STATE 1 -> STATE 2
}
return new_state;
}
Existe-t-il un meilleur moyen pour les machines à états simples
EDIT: Pour C ++, je pense que la bibliothèque Boost Statechart pourrait être la voie à suivre. Cependant, cela n'aide pas avec C. Concentrons-nous sur le cas d'utilisation C.
c
design-patterns
finite-automata
Benoit
la source
la source
Réponses:
Je préfère utiliser une approche basée sur une table pour la plupart des machines à états:
Cela peut bien sûr être étendu pour prendre en charge plusieurs machines à états, etc. Les actions de transition peuvent également être prises en charge:
L'approche basée sur les tables est plus facile à maintenir et à étendre et plus simple à mapper aux diagrammes d'état.
la source
Vous avez peut-être vu ma réponse à une autre question en C où j'ai mentionné FSM! Voici comment je le fais:
Avec les macros suivantes définies
Cela peut être modifié en fonction du cas spécifique. Par exemple, vous pouvez avoir un fichier
FSMFILE
que vous souhaitez conduire votre FSM, vous pouvez donc incorporer l'action de lecture du prochain caractère dans la macro elle-même:maintenant vous avez deux types de transitions: l'une passe à un état et lit un nouveau caractère, l'autre passe à un état sans consommer aucune entrée.
Vous pouvez également automatiser la gestion d'EOF avec quelque chose comme:
L'avantage de cette approche est que vous pouvez directement traduire un diagramme d'état que vous dessinez en code de travail et, inversement, vous pouvez facilement dessiner un diagramme d'état à partir du code.
Dans d'autres techniques d'implémentation de FSM, la structure des transitions est enterrée dans des structures de contrôle (tandis que, si, switch ...) et contrôlée par des variables value (typiquement une
state
variable) et il peut être une tâche complexe de relier le joli diagramme à un code alambiqué.J'ai appris cette technique à partir d'un article paru dans le grand magazine "Computer Language" qui, malheureusement, n'est plus publié.
la source
J'ai également utilisé l'approche de la table. Cependant, il y a des frais généraux. Pourquoi stocker une deuxième liste de pointeurs? Une fonction en C sans le () est un pointeur const. Vous pouvez donc faire:
Bien sûr, en fonction de votre facteur de peur (c'est-à-dire sécurité vs vitesse), vous voudrez peut-être vérifier les pointeurs valides. Pour les machines à états de plus de trois états environ, l'approche ci-dessus devrait être moins d'instructions qu'une approche de commutateur ou de table équivalente. Vous pouvez même effectuer des macros comme:
De plus, je pense, d'après l'exemple de l'OP, qu'il y a une simplification à faire lors de la réflexion / conception d'une machine à états. Je ne pense pas que l'état de transition devrait être utilisé pour la logique. Chaque fonction d'état devrait être capable de remplir son rôle donné sans connaissance explicite de l'état ou des états passés. Fondamentalement, vous concevez comment passer de l'état dans lequel vous vous trouvez à un autre état.
Enfin, ne commencez pas la conception d'une machine à états basée sur des frontières «fonctionnelles», utilisez des sous-fonctions pour cela. Au lieu de cela, divisez les états en fonction du moment où vous devrez attendre que quelque chose se produise avant de pouvoir continuer. Cela aidera à minimiser le nombre de fois où vous devrez exécuter la machine à états avant d'obtenir un résultat. Cela peut être important lors de l'écriture de fonctions d'E / S ou de gestionnaires d'interruption.
En outre, quelques avantages et inconvénients de la déclaration de commutateur classique:
Avantages:
Les inconvénients:
Notez les deux attributs qui sont à la fois pour et contre. Je pense que le changement permet un trop grand partage entre les États, et l'interdépendance entre les États peut devenir ingérable. Cependant, pour un petit nombre d'états, il peut être le plus lisible et maintenable.
la source
Pour une machine à états simple, utilisez simplement une instruction switch et un type enum pour votre état. Effectuez vos transitions dans l'instruction switch en fonction de votre entrée. Dans un vrai programme, vous changeriez évidemment le "if (input)" pour vérifier vos points de transition. J'espère que cela t'aides.
la source
Dans UML Distilled de Martin Fowler , il déclare (sans jeu de mots) dans le chapitre 10 State Machine Diagrams (c'est moi qui souligne):
Prenons un exemple simplifié des états de l'écran d'un téléphone mobile:
Commutateur imbriqué
Fowler a donné un exemple de code C #, mais je l'ai adapté à mon exemple.
Modèle d'état
Voici une implémentation de mon exemple avec le modèle d'état GoF:
Tables d'états
En s'inspirant de Fowler, voici un tableau pour mon exemple:
Comparaison
Le commutateur imbriqué conserve toute la logique au même endroit, mais le code peut être difficile à lire lorsqu'il y a beaucoup d'états et de transitions. C'est peut-être plus sûr et plus facile à valider que les autres approches (pas de polymorphisme ni d'interprétation).
L'implémentation du modèle d'état étend potentiellement la logique sur plusieurs classes distinctes, ce qui peut rendre sa compréhension dans son ensemble problématique. D'autre part, les petites classes sont faciles à comprendre séparément. La conception est particulièrement fragile si vous modifiez le comportement en ajoutant ou en supprimant des transitions, car ce sont des méthodes dans la hiérarchie et il peut y avoir beaucoup de changements dans le code. Si vous vivez selon le principe de conception des petites interfaces, vous verrez que ce modèle ne fonctionne pas vraiment bien. Cependant, si la machine à états est stable, de telles modifications ne seront pas nécessaires.
L'approche des tables d'états nécessite l'écriture d'une sorte d'interpréteur pour le contenu (cela peut être plus facile si vous avez une réflexion dans la langue que vous utilisez), ce qui pourrait être beaucoup de travail à faire au départ. Comme le souligne Fowler, si votre table est séparée de votre code, vous pouvez modifier le comportement de votre logiciel sans recompilation. Cela a cependant des implications sur la sécurité; le logiciel se comporte en fonction du contenu d'un fichier externe.
Edit (pas vraiment pour le langage C)
Il existe également une approche d'interface fluide (aka interne Domain Specific Language), qui est probablement facilitée par des langages dotés de fonctions de premier ordre . La bibliothèque Stateless existe et ce blog montre un exemple simple avec du code. Une implémentation Java (pré Java8) est discutée. On m'a également montré un exemple Python sur GitHub .
la source
il y a aussi la grille logique qui est plus maintenable à mesure que la machine d'état devient plus grande
la source
Pour les cas simples, vous pouvez utiliser votre méthode de style de commutation. Ce que j'ai trouvé qui fonctionne bien dans le passé, c'est de gérer également les transitions:
Je ne sais rien de la bibliothèque boost, mais ce type d'approche est extrêmement simple, ne nécessite aucune dépendance externe et est facile à implémenter.
la source
switch () est un moyen puissant et standard d'implémenter des machines d'état en C, mais il peut diminuer la maintenabilité si vous avez un grand nombre d'états. Une autre méthode courante consiste à utiliser des pointeurs de fonction pour stocker l'état suivant. Cet exemple simple implémente une bascule set / reset:
la source
J'ai trouvé une implémentation C vraiment astucieuse de Moore FSM sur le cours edx.org Embedded Systems - Shape the World UTAustinX - UT.6.02x, chapitre 10, par Jonathan Valvano et Ramesh Yerraballi ....
la source
Vous voudrez peut-être consulter le logiciel générateur libero FSM. A partir d'un langage de description d'état et / ou d'un éditeur de diagramme d'état (Windows), vous pouvez générer du code pour C, C ++, Java et bien d'autres ... ainsi qu'une documentation et des diagrammes sympas. Source et binaires d' iMatix
la source
Cet article est un bon pour le modèle d'état (bien qu'il soit C ++, pas spécifiquement C).
Si vous pouvez mettre la main sur le livre " Head First Design Patterns ", l'explication et l'exemple sont très clairs.
la source
L'un de mes modèles préférés est le modèle de conception d'état. Répondre ou se comporter différemment au même ensemble d'entrées donné.
L'un des problèmes liés à l'utilisation des instructions switch / case pour les machines à états est qu'au fur et à mesure que vous créez plus d'états, le switch / cases devient plus difficile / difficile à lire / maintenir, favorise un code spaghetti non organisé et de plus en plus difficile à changer sans casser quelque chose. Je trouve que l'utilisation de modèles de conception m'aide à mieux organiser mes données, ce qui est tout le but de l'abstraction. Au lieu de concevoir votre code d'état en fonction de l'état dont vous venez, structurez plutôt votre code de sorte qu'il enregistre l'état lorsque vous entrez dans un nouvel état. De cette façon, vous obtenez effectivement un enregistrement de votre état antérieur. J'aime la réponse de @ JoshPetit et j'ai poussé sa solution un peu plus loin, tirée directement du livre du GoF:
stateCtxt.h:
stateCtxt.c:
statehandlers.h:
statehandlers.c:
Pour la plupart des machines d'état, en particulier. Machines à états finis, chaque état saura quel devrait être son prochain état et les critères de transition vers son état suivant. Pour les conceptions d'état lâche, cela peut ne pas être le cas, d'où la possibilité d'exposer l'API pour les états de transition. Si vous désirez plus d'abstraction, chaque gestionnaire d'état peut être séparé dans son propre fichier, qui est équivalent aux gestionnaires d'états concrets du livre GoF. Si votre conception est simple avec seulement quelques états, alors stateCtxt.c et statehandlers.c peuvent être combinés en un seul fichier pour plus de simplicité.
la source
D'après mon expérience, l'utilisation de l'instruction «switch» est le moyen standard de gérer plusieurs états possibles. Bien que je sois surpris que vous transmettez une valeur de transition au traitement par état. Je pensais que l'intérêt d'une machine à états était que chaque état effectuait une seule action. Ensuite, l'action / l'entrée suivante détermine le nouvel état vers lequel effectuer la transition. Donc, je me serais attendu à ce que chaque fonction de traitement d'état effectue immédiatement tout ce qui est fixé pour entrer dans l'état, puis décide ensuite si une transition est nécessaire vers un autre état.
la source
Il existe un livre intitulé Practical Statecharts in C / C ++ . Cependant, il est bien trop lourd pour ce dont nous avons besoin.
la source
Pour le compilateur qui prend en charge
__COUNTER__
, vous pouvez les utiliser pour des mashines d'état simples (mais volumineux).L'avantage d'utiliser à la
__COUNTER__
place des nombres codés en dur est que vous pouvez ajouter des états au milieu d'autres états, sans renuméroter à chaque fois tout. Si le compilateur ne prend pas en charge__COUNTER__
, de manière limitée, il est possible de l'utiliser avec précaution__LINE__
la source
__COUNTER__
élimine le besoin de renuméroter, parce que le précompilateur fait la numérotation pendant la compilation.Vous pouvez utiliser un framework de machine d'état UML minimaliste dans c. https://github.com/kiishor/UML-State-Machine-in-C
Il prend en charge la machine à états finie et hiérarchique. Il n'a que 3 API, 2 structures et 1 énumération.
La machine à états est représentée par la
state_machine_t
structure. C'est une structure abstraite qui peut être héritée pour créer une machine à états.L'état est représenté par un pointeur vers la
state_t
structure dans le cadre.Si le framework est configuré pour la machine à états finis
state_t
contient alors ,Le framework fournit une API
dispatch_event
pour distribuer l'événement à la machine d'état et deux API pour la traversée d'état.Pour plus de détails sur la manière d'implémenter la machine d'état hiérarchique, reportez-vous au référentiel GitHub.
exemples de code
https://github.com/kiishor/UML-State-Machine-in-C/blob/master/demo/simple_state_machine/readme.md
https://github.com/kiishor/UML-State-Machine-in -C / blob / master / demo / simple_state_machine_enhanced / readme.md
la source
En C ++, considérez le modèle State .
la source
Votre question est similaire à "existe-t-il un modèle d'implémentation de base de données typique"? La réponse dépend de ce que vous voulez réaliser? Si vous souhaitez implémenter une machine à états déterministe plus grande, vous pouvez utiliser un modèle et un générateur de machine à états. Des exemples peuvent être consultés sur www.StateSoft.org - SM Gallery. Janusz Dobrowolski
la source
Boost a la bibliothèque d'états. http://www.boost.org/doc/libs/1_36_0/libs/statechart/doc/index.html
Cependant, je ne peux pas en parler. Je ne l'ai pas (encore) utilisé moi-même
la source