203 votes

C conception de machine d’État

Je suis d'artisanat d'un petit projet en mixte C et C++. Je fais construire une petite-ish de machine d'état au cœur de l'un de mes thread de travail.

Je me demandais si vous gourous sur serait de partager votre machine d'état des techniques de conception.

NOTE: je suis surtout après avoir essayé et testé des techniques de mise en œuvre.

Mise à JOUR: Basé sur tous les grands commentaires recueillis sur DONC, j'ai installé sur cette architecture:

alt text

183voto

paxdiablo Points 341644

État des machines que j'ai conçu avant (C, pas du C++) ont tous descendent d'un struct tableau et une boucle. La structure se compose essentiellement d'un état et de l'événement (pour la recherche) et une fonction qui renvoie le nouvel état, quelque chose comme:

typedef struct {
    int st;
    int ev;
    int (*fn)(void);
} tTransition;

Ensuite, vous définissez vos états et les événements avec de simples définit ( ANY sont des marqueurs spéciaux, voir ci-dessous):

#define ST_ANY              -1
#define ST_INIT              0
#define ST_ERROR             1
#define ST_TERM              2
: :
#define EV_ANY              -1
#define EV_KEYPRESS       5000
#define EV_MOUSEMOVE      5001

Ensuite, vous définissez toutes les fonctions qui sont appelées par les transitions:

static int GotKey (void) { ... };
static int FsmError (void) { ... };

Toutes ces fonctions sont écrites de ne pas prendre de variables et retour le nouvel état de la machine d'état. Parce qu'ils suivent tous la même forme et ne pas prendre les paramètres, de l'utilisation qui est faite de "global", les variables de l'information en passant, si nécessaire. Ce n'est pas aussi mauvais que cela puisse paraître, depuis le FSM est généralement enfermé à l'intérieur d'une seule unité de compilation et toutes les variables sont statiques à l'unité (c'est pourquoi j'ai utilisé des guillemets autour de "global" ci - dessus, ils ne sont plus partagées que véritablement mondiale). Comme avec toutes les variables globales, il nécessite des soins.

Les transitions tableau définit ensuite toutes les transitions possibles et les fonctions get appelée pour ces transitions (y compris le fourre-tout dernier):

tTransition trans[] = {
    { ST_INIT, EV_KEYPRESS, &GotKey},
    : :
    { ST_ANY, EV_ANY, &FsmError}
};
#define TRANS_COUNT (sizeof(trans)/sizeof(*trans))

Le fonctionnement de la FSM devient alors relativement simple boucle:

state = ST_INIT;
while (state != ST_TERM) {
    event = GetNextEvent();
    for (i = 0; i < TRANS_COUNT; i++) {
        if ((state == trans[i].st) || (ST_ANY == trans[i].st)) {
            if ((event == trans[i].ev) || (EV_ANY == trans[i].ev)) {
                state = (trans[i].fn)();
                break;
            }
        }
    }
}

Comme mentionné ci-dessus, note l'utilisation de l' ST_ANY et EV_ANY comme des caractères génériques, permettant à une manifestation pour appeler une fonction peu importe l'état actuel, et de garantir que, si vous atteignez la fin de la transition tableau, vous obtenez un message d'erreur indiquant votre FSM n'a pas été correctement construit.

J'ai utilisé un code similaire pour cela sur un grand nombre de projets de communication, comme un début de mise en œuvre de l'OSI modèle en couches et protocoles pour les systèmes embarqués. C'est gros avantage est sa simplicité et la facilité relative à la modification de la transitions tableau.

Je n'ai aucun doute il y aura des abstractions de niveau supérieur qui peut être plus approprié de nos jours, mais je soupçonne qu'ils vont tous se résument à ce même genre de structure.


En tant que ldog états dans un commentaire, vous pouvez éviter les variables globales tout en passant un pointeur de structure à toutes les fonctions (et les utiliser que dans le cas de la boucle). Cela permettra à plusieurs machines d'état à exécuter côte à côte, sans interférence.

Il suffit de créer un type de structure qui tient la machine les données spécifiques de l'etat (au minimum) et d'utiliser à la place de la globals.

La raison pour laquelle j'ai rarement fait c'est simplement parce que la plupart des machines d'état que j'ai écrits ont été singleton types (one-off, les processus de démarrage, le fichier de configuration de la lecture par exemple), pas besoin d'exécuter plus d'une instance. Mais il a de la valeur si vous avez besoin d'exécuter plus d'un.

85voto

caf Points 114951

Les autres réponses sont bonnes, mais ressemble à une implémentation très « léger », que je l’ai utilisé lorsque l’ordinateur d’État est très simple :

Je voudrais utiliser ceci lorsque l’ordinateur d’État est assez simple pour que la fonction pointeur & État transition approche par table est excessif. C’est souvent utile pour l’analyse de caractère par caractère ou mot par mot.

42voto

Jason E Points 435

Pardonnez-moi de briser toutes les règles en informatique, mais une machine d'état est l'un des rares (je peux compter seulement deux outre de la main) les lieux où un goto déclaration n'est pas seulement plus efficace, mais aussi rend votre code plus propre et plus facile à lire. Parce qu' goto déclarations sont fondées sur les etiquettes, vous pouvez nommer vos états au lieu d'avoir à garder une trace d'un désordre de numéros ou d'utiliser un enum. Il est également beaucoup plus propre code, puisque vous n'avez pas besoin de tous les fichiers inutiles de pointeurs de fonction ou des instructions de commutation et des boucles while. Ai-je mentionné que c'est plus efficace?

Voici ce qu'une machine d'état pourrait ressembler à:

void state_machine() {
first_state:
    // Do some stuff here
    switch(some_var) {
    case 0:
        goto first_state;
    case 1:
        goto second_state;
    default:
        return;
    }

second_state:
    // Do some stuff here
    switch(some_var) {
    case 0:
        goto first_state;
    case 1:
        goto second_state;
    default:
        return;
    }
}

Vous avez l'idée générale. Le point est que vous pouvez mettre en œuvre l'état de la machine dans une façon efficace et relativement facile à lire et à cris au lecteur qu'ils sont à la recherche à une machine d'état. Notez que si vous utilisez goto des déclarations, vous devez toujours être prudent, car il est très facile de se tirer dans le pied en faisant de la sorte.

31voto

willw Points 1026

Vous pourriez envisager de la Machine d'État du Compilateur http://smc.sourceforge.net/

Ce splendide utilitaire open source accepte une description d'une machine d'état dans un langage simple et compile à quelqu'un d'une douzaine de langues - dont le C et le C++. L'utilitaire lui-même est écrit en Java, et peut être inclus en tant que partie d'une génération.

La raison pour ce faire, plutôt que de codage à la main à l'aide de GoF de l'État de modèle ou de toute autre approche, est qu'une fois que votre machine à état est exprimé en tant que code, la structure sous-jacente tend à disparaître sous le poids de code qui doivent être produites à l'appui. L'utilisation de cette approche vous donne une excellente séparation des préoccupations, et vous gardez la structure de votre machine d'état "visibles". Le code généré automatiquement va dans les modules que vous n'avez pas besoin de toucher, de sorte que vous pouvez revenir en arrière et jouer avec la machine d'état de la structure, sans impact sur le code de prise en charge que vous avez écrit.

Désolé, je suis trop enthousiaste, et sans doute, en mettant tout le monde. Mais il est un excellent utilitaire, et bien documenté.

21voto

Daniel Daranas Points 15123

Assurez-vous de vérifier le travail de Miro Samek, dont les articles sur le C/C++ Utilisateurs de Journal ont été formidables.

Le livre et le site contient une complète (C/C++) mise en œuvre à la fois open source et licence commerciale d'une machine d'état du cadre (QP Cadre), un gestionnaire d'événements (QEP), une modélisation de base de l'outil (QM) et un outil de traçage (QSpy) , qui permettent de faire l'état des machines, créer un code et déboguer.

Le livre contient une longue explication sur le quoi de la mise en œuvre et la façon de l'utiliser et est également le matériau idéal pour mieux comprendre les fondements de l'hiérarchique et des machines à états finis.

Le site web contient également des liens vers plusieurs board support packages pour l'utilisation du logiciel avec les plateformes embarquées.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X