{"id":17446983,"url":"https://github.com/nhjschulz/cfsm","last_synced_at":"2025-04-19T14:35:47.223Z","repository":{"id":221856922,"uuid":"755527998","full_name":"nhjschulz/cfsm","owner":"nhjschulz","description":"CFSM - A State Design Pattern for State Machines in C-Language.","archived":false,"fork":false,"pushed_at":"2024-10-30T19:11:30.000Z","size":183,"stargazers_count":15,"open_issues_count":0,"forks_count":2,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-29T08:43:37.455Z","etag":null,"topics":["c-programming","patterns","state-machine"],"latest_commit_sha":null,"homepage":"https://nhjschulz.github.io/cfsm/","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nhjschulz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-02-10T13:43:32.000Z","updated_at":"2025-03-17T09:38:30.000Z","dependencies_parsed_at":"2024-10-21T15:41:25.053Z","dependency_job_id":null,"html_url":"https://github.com/nhjschulz/cfsm","commit_stats":null,"previous_names":["nhjschulz/cfsm"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nhjschulz%2Fcfsm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nhjschulz%2Fcfsm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nhjschulz%2Fcfsm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nhjschulz%2Fcfsm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nhjschulz","download_url":"https://codeload.github.com/nhjschulz/cfsm/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249715584,"owners_count":21315056,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c-programming","patterns","state-machine"],"created_at":"2024-10-17T19:04:57.257Z","updated_at":"2025-04-19T14:35:47.204Z","avatar_url":"https://github.com/nhjschulz.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![License](https://img.shields.io/badge/license-MIT-blue.svg)](http://choosealicense.com/licenses/mit/)\n[![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active)\n![example workflow](https://github.com/nhjschulz/cfsm/actions/workflows/cmake-single-platform.yml/badge.svg)\n[![PlatformIO Registry](https://badges.registry.platformio.org/packages/nhjschulz/library/CFSM.svg)](https://registry.platformio.org/libraries/nhjschulz/CFSM)\n[![arduino-library-badge](https://www.ardu-badge.com/badge/CFSM.svg?)](https://www.ardu-badge.com/CFSM)\n\n# CFSM - A State Design Pattern for State Machines in C-Language.\n\nFinite state machines (FSM) are present in almost every non trivial program.\nGuides on how to implement them are part of many programming\ntutorials. But these tutorials focus around the\n[STATE Design Pattern](https://en.wikipedia.org/wiki/State_pattern)\nfor \u003cb\u003eobject oriented languages\u003c/b\u003e like C++, Java or C# only.\n\nCFSM follows a simplistic approach for the C-Language to implement\nmaintainable state machines according to the STATE design pattern.\nThis differentiates it from other solutions that often rely on complex\nmacros to construct state handlers.\n\nThis work was inspired by the excellent article\n[Patterns in C- Part 2: STATE](https://www.adamtornhill.com/Patterns%20in%20C%202,%20STATE.pdf) from Adam Petersen.\n\n## Introducing CFSM\n\nThe CFSM Github project contains both\nthe CFSM source code and an easy to understand example. It is build using\n[CMake](https://cmake.org). \u003cbr\u003e\nIntegration into own projects doesn't require CMake. CFSM is a single source\nand header file only. There are no external dependencies. Simply add the\n[c_fsm.c and c_fsm.h](https://github.com/nhjschulz/cfsm/tree/master/src)\nfiles into your project.\n\n## How It Works\n\nCFSM is following the State design pattern without using object oriented\nconstructs.\n\n![State Pattern](http://www.plantuml.com/plantuml/proxy?src=https://raw.githubusercontent.com/nhjschulz/cfsm/master/doc/cfsm_statepattern.puml)\n\nThe state pattern builds on\n* A context that delegates operations to one of various state objects,\n  which is currently the active state.\n* A number of state objects that implement context operations to provide\n  state dependent behavior of these operations.\n\n### The CFSM Context\n\nA CFSM context defines a fixed set of operations. These operations got defined\nwith the following UML State diagram in mind. It covers a wide range of\nuse cases:\n\n![State Diagram](http://www.plantuml.com/plantuml/proxy?src=https://raw.githubusercontent.com/nhjschulz/cfsm/master/doc/cfsm_context.puml)\n\nThere are operations to execute\n   1. When the FSM \"enters\" a state, meaning it becomes the current active one.\n   2. When the FSM \"leaves\" a state, meaning some other state becomes active\n   3. When a cyclic processing in the active state shall take place\n   4. When an event is signaled to the FSM\n\nEach of these operations is represented as a function pointer in the CFSM\ncontext data structure. CFSM takes care about calling leave and enter\noperations during a state transition. The cyclic process and event signaling\ngets triggered by the application through CFSM public API.\n\nA void pointer called ```ctxPtr``` is part of the context in addition to\nthe operation function pointers. This pointer is set by ```cfm_init()``` and\ncan be used in an application specific way. FSM does not use it by itself.\nThe ```ctxPtr``` is intended to support multiple FSM instances with the same\nhandler functions. The handlers can use the pointer to access instance\nspecific data.\n\nThe CFSM context structure definition is:\n\n```c\ntypedef void (*cfsm_TransitionFunction)(struct cfsm_Ctx * fsm);\ntypedef void (*cfsm_EventFunction)(struct cfsm_Ctx * fsm, int eventId);\ntypedef void (*cfsm_ProcessFunction)(struct cfsm_Ctx * fsm);\ntypedef void *cfsm_InstanceDataPtr;\n\n/** CFSM context operations */\ntypedef struct cfsm_Ctx {\n    cfsm_InstanceDataPtr    ctxPtr;    /**\u003c context instance data     */\n    cfsm_TransitionFunction onLeave;   /**\u003c operation run on leave    */\n    cfsm_ProcessFunction    onProcess; /**\u003c cyclic operations         */\n    cfsm_EventFunction      onEvent;   /**\u003c report event to the state */\n} cfsm_Ctx;\n\n```\n\nNotes:\n * All operations in a state are optional, with the exception of the enter\n   operation. A state that cannot be entered is pointless.\n * Operations that are not defined in a state are ignored by CFSM.\n * Supporting \"other\" operations can be done by adding new, or\n   changing existing functions pointers in the context. CFSM\n   is primarily an implementation pattern, not a fixed function library.\n\n### CFSM States\n\nA CFSM state is a light weight concept. It is not implemented as\nan object or data structure as someone would expect using object\noriented languages. A state in the C-Language world is just a set of\nfunctions that are known by the context as operations.\n\nThe only mandatory state function is the enter operation. It is needed\neven if there are no state specific entry actions to perform. It's job is\nto also update the context function pointers. Below is an example of a\nset of function that define a SuperMario state:\n\n```c\nstatic void SuperMario_onProcess(cfsm_Ctx * fsm) { /* ... */}\nstatic void SuperMario_onLeave(cfsm_Ctx * fsm) { /* ... */}\nstatic void SuperMario_onEvent(cfsm_Ctx * fsm, int eventId) { /* ...*/}\n\n\nvoid SuperMario_onEnter(cfsm_Ctx * fsm)\n{\n    fsm-\u003eonProcess = SuperMario_onProcess;\n    fsm-\u003eonEvent = SuperMario_onEvent;\n    fsm-\u003eonLeave = SuperMario_onLeave;\n    /* ... */\n\n}\n```\n\n### CFSM State Transitions\n\nState transitions are triggered by calling the ```cfsm_transition()```\nAPI function. The call can originate\n\n1. from the CFSM using application to enter a specific state\n2. from inside the state operation handlers\n\nThe following interaction diagram shows what happens during a state\ntransition from state \"Mario\" to \"SuperMario\":\n\n![Transition Diagram](http://www.plantuml.com/plantuml/proxy?src=https://raw.githubusercontent.com/nhjschulz/cfsm/master/doc/cfsm_transition.puml)\n\nThe process() calls are not part of the transition. They show how\nthe delegation of the process action changed as a result of the\nstate transition in between.\n\n## Examples\n\nThe remainder of this document walks through the Mario example to\ndemonstrate CFSM usage. There is also a working CFSM version of the\nArduino blink sketch worth a look. It is available from\n[https://github.com/nhjschulz/cfsm/tree/master/examples/UnoBlink](https://github.com/nhjschulz/cfsm/tree/master/examples/UnoBlink). This minimal example\nis suitable as a boilerplate for own CFSM based application experiments.\n\n# The Mario CFSM Example\n\nIn this chapter we use the CFSM pattern to simulate the life cycle of the\nfamous Mario computer game character that most people should be familiar with.\n\n## The Mario State Machine\n\nMario can change his appearance into various different characters to gain\nsuper powers. He starts as small Mario without powers. He changes\nto a different appearance with a specific power by collecting items.\nMario earns coins by collecting items and gets an additional life\nif he collects more than 5000 of them.\nIf an empowered Mario hits a monster, he loses the power and changes\nback to small Mario. If small Mario hits a monster, he loses a life. If\nno more lives are available, the game ends.\n\nThis means we have a fixed set of states (characters) and rules (items)\nthat define how to switch between them. This is a FSM! We can model\nit like this:\n\n![Mario State Diagram](http://www.plantuml.com/plantuml/proxy?src=https://raw.githubusercontent.com/nhjschulz/cfsm/master/doc/mario_states.puml)\n\nHere are the transitions shown as a table:\n\n| Character  | Item       | Effect            | Coins |\n|------------|------------|-------------------|-------|\n|Small Mario |Mushroom    |Become Super Mario | +100  |\n|Small Mario |Fire Flower |Become Fire Mario  | +200  |\n|Small Mario |Feather     |Become Cape Mario  | +300  |\n|Small Mario |Monster     |Loose a Life       |       |\n|Super Mario |Fire Flower |Become Fire Mario  | +200  |\n|Super Mario |Feather     |Become Cape Mario  | +300  |\n|Super Mario |Monster     |Become Small Mario |       |\n|Fire Mario  |Feather     |Become Cape Mario  | +300  |\n|Fire Mario  |Monster     |Become Small Mario |       |\n|Cape Mario  |Fire Flower |Become Fire Mario  | +200  |\n|Cape Mario  |Monster     |Become Small Mario |       |\n\n## Example Implementation\n\nThe example uses CFSM to implement the Mario state machine in the following\nway:\n\n  * Each Mario character is a state implemented in an own C-File.\n  * The collection of items or the monster hits are modelled as events.\n  * The process operation prints a character specific message.\n  * The enter/leave operations print a message to visualize these transitions.\n\nThe main loop of the example implements a small menu where events get\nfired based on user input to simulate the game.\n\nThis is the example application component design:\n\n![Mario Example Class Diagram](http://www.plantuml.com/plantuml/proxy?src=https://raw.githubusercontent.com/nhjschulz/cfsm/master/doc/mario_classdiagram.puml)\n\n\n### The Main function\n\nThe main function implements the game simulation loop. It owns\na CFSM instance as a local cfsm_Ctx structure called ``marioFsm``.\n\nThe CFSM setup phase consists of initializing ``marioFsm`` and\nthen transition to Mario's start state \"SmallMario\". The simplified\ncodes looks like this:\n\n ```c\n#include \"c_fsm.h\"\n#include \"states/small_mario.h\"\n\nint main(int argc, char **argv)\n{\n    cfsm_Ctx marioFsm;\n\n    cfsm_init(\u0026marioFsm, NULL);\n    cfsm_transition(\u0026marioFsm, SmallMario_onEnter);\n\n    /* ... */\n ```\n\nThis example doesn't use instance data and passes NULL for it\nin the call to ```cfsm_init()```. Instance data could be used to\nextend the game to support multiple players by adding\na Luigi. We can then run two FSMs in parallel with the same\nhandlers.\n\nTransitioning to the start state is done by providing the\nenter operation handler for this state to the API function\n``cfsm_transition()``. CFSM then calls the leave operation\nof the former state (if one was defined) and then calls the passed\nenter operation.\n\nThe remainder of the main function is the game loop. It gives Mario\na process operation cycle by calling\n``cfsm_process(\u0026marioFsm)``. The process handlers in this example\nonly print a message according to Mario's current state to\nshow that they had been run.\u003cbr\u003e\nThe loop then displays a menu asking to enter a key to trigger\nthe next event. This event is passed to the Mario CFSM by calling\n``cfsm_event()``. \u003cbr\u003e\nFinally the loop restarts unless QUIT was selected by the user.\n\n ```c\n    for(;;)\n    {\n        int option;\n\n        /* perform process cycle in current CFSM state.*/\n        cfsm_process(\u0026marioFsm);\n\n        mario_print(); /* show Mario's data*/\n\n        printf(\"\\nChoose Event: (1=Mushroom, 2=FireFlower, 3=feather, \"\n               \"4=Monster, 5=none (just process), 0=quit) : \");\n        while ((scanf(\"%d\", \u0026option) != 1) || (option \u003e 5))\n        {\n            puts(\"invalid option\");\n        }\n\n        if (QUIT == option) {\n            break;\n        }\n\n        /* process signal in current CFSM state */\n        if (NOP != option)\n        {\n            cfsm_event(\u0026marioFsm, option);\n        }\n    }\n ```\n\nThat's it. There is no special state processing in the application\nmain loop. It's only purpose is to initialize, process and signal\nevents to CFSM. Any state dependent code vanishes into the\ndifferent state implementing modules.\n\nHere is a transcript of first game simulation steps:\n\n ```\n $ cfsm_mario.exe\n(1) SmallMario_onEnter()...\n(2) SmallMario_onProces(): It's me, Mario!\n(3) Mario: Variant: SmallMario Lifes: 3  Coins: 0\n\n(4) Choose Event: (1=Mushroom, 2=FireFlower, 3=feather, 4=Monster, 5=none (just process) 0=quit) : 1\n(5) SmallMario_onLeave() ...\n(5) SuperMario_onEnter()...\n(6) SuperMario_onProces(): It's me, SUPER Mario!\n(7) Mario: Variant: SuperMario Lifes: 3  Coins: 100\n\n(8) Choose Event: (1=Mushroom, 2=FireFlower, 3=feather, 4=Monster, 5=none (just process) 0=quit) :\n ```\n\n1. This is the response of the initial transition to small Mario. Note that\n   there is no leave message as CFSM had no former state.\n2. This is the response to the process cycle we trigger at the begin of\n   the game loop.\n3. This is main printing the current Mario state. It is not related to CFSM.\n4. This is the menu, asking for user input. We gave it a 1 (MUSHROOM).\n5. Processing the MUSHROOM event causes a transition inside the event\n   operation of SmallMario. We leave SmallMario state into SuperMario.\n6. Now the loop restarted with the process cycle. Note that we became\n   SUPER Mario :).\n7. Also Mario's data got updated. It shows now SuperMario and the earned\n   100 coins.\n8. The game loop askes again for the next user input\n\n### The Event States Implementation\n\nAll states from the example are implemented in an own C module in the\nsrc/example/states folder. There is no requirement to do it this way,\nbut its a reasonable way to keep things maintainable if state complexity\nor number increases over time. The following sub chapters walk through\nthe operation handlers of the small Mario state. The other states are\nvery similar and not shown here. Also remember that a CFSM state is just\na set of operation handler functions.\n\n#### The Small Mario Enter Operation\n\nThe enter function is the only mandatory operation handler for a state\nand the only one that needs to be public. This is necessary to allow\nother modules to transition into it.\n\n ```c\n void SmallMario_onEnter(cfsm_Ctx * fsm)\n{\n    puts(\"SmallMario_onEnter()...\");\n\n    mario_setVariant(SMALL_MARIO);\n\n    fsm-\u003eonProcess = SmallMario_onProcess;\n    fsm-\u003eonEvent = SmallMario_onEvent;\n    fsm-\u003eonLeave = SmallMario_onLeave;\n}\n ```\n\n The enter operation gets the CFSM context passed as a pointer. This\n is required to update the handler pointers to use the new states\n operations.\n\n The first two lines are the actions that the enter handler performs.\n In this example it\n\n * prints a message to show the user it got called. In real code such\n   a call would not be there or would be using some debug logging api.\n * updates our Mario to become a small one.\n\nThe final 3 lines update the CFSM context to delegate operations\nto the SmallMario state.\n\nNote that unused handlers don't need to be set to NULL. The\n``cfsm_transition()`` API has done this before calling the\nenter operation. Only the needed handlers must be set here\n(if any).\n\n#### The Small Mario Leave Operation\n\nOur example leave operations are trivial. We have no actions to perform\naccording to the Mario state machine. They just print a line to indicate\nto the user when they got called.\n\n```c\nstatic void SmallMario_onLeave(cfsm_Ctx * fsm)\n{\n    puts(\"SmallMario_onLeave() ...\");\n}\n ```\n\n#### The Small Mario Process Operation\n\nOur process operations are also trivial. They just print a line\nthat fits to the current Mario personality.\n\nIn real applications you have to make a decision what to do during\ncyclic processing or event signaling. Our Mario model is event\ndriven. That's why the transition logic and action where placed into\nthe event operation handler. If your logic follows a polling model,\nyou likely implement this in the processing operation instead.\n\n```c\nstatic void SmallMario_onProcess(cfsm_Ctx * fsm)\n{\n    puts(\"SmallMario_onProces(): It's me, Mario!\");\n}\n ```\n\n#### The Small Mario Event Signal Operation\n\nThe onEvent operation is the working horse in our example Mario state\nmachine due to the fact that all transitions are event based. The\nevent signal operation is implemented as a switch over the event ids.\n\nThe call to ``mario_updateCoins()`` extracts the coin awards into\na helper function. The amount of coins depend on the event, not\non the state. Directly implementing it inside the states would cause\ncode dublication.\n\nThe individual event cases trigger a transition from small to another\nMario dependent on the event. Noteworthy is the slightly more complex\nmonster case. Here we also need to decrease the number of lives\nand eventually transition into dead Mario if no more are left.\n\n```c\nstatic void SmallMario_onEvent(cfsm_Ctx * fsm, int eventId)\n{\n    mario_updateCoins(eventId);\n\n    switch(eventId)\n    {\n        case MUSHROOM:\n            cfsm_transition(fsm, SuperMario_onEnter);\n            break;\n\n        case FIREFLOWER:\n            cfsm_transition(fsm, FireMario_onEnter);\n            break;\n\n        case FEATHER:\n            cfsm_transition(fsm, CapeMario_onEnter);\n            break;\n\n        case MONSTER:\n            if (0 == mario_takeLife())\n            {\n                cfsm_transition(fsm, DeadMario_onEnter);\n            }\n            break;\n    }\n}\n ```\n\nThis is now a good time to look into the other Mario [state\nimplementations](https://github.com/nhjschulz/cfsm/tree/master/examples/mario/states)\nto figure out how they differ from SmallMario.\nThe DeadMario state is the most deviating one. There is no way\nback from the after live, meaning some operation handlers are\nnot needed and therefore also not present at all.\n\n# Conclusion\n\nThe CFSM pattern is surprisingly simple. There are no complex logic\nsequences or loops in CFSM. Processing boils down to a NULL checked\nfunction pointer call to delegate operation requests to state\ndependend handlers. This simplicity makes the pattern also usable \nfor functional safety applications. The functionality is easy to\ntest and review.\n\n## Benefits\n\nCFSM achieves the following benefits\n\n1. Shows a light weight method to implement the STATE pattern in plain C\n   language.\n2. Avoids complex and nested conditional logic by distributing it\n   into different states that are easier to maintain.\n3. Encapsulates state specific behavior into the states implementation.\n4. Easy to extend with new states. Adding new states affect the\n   existing code in a minimal way. Only transitions to the new state\n   need to be added somewhere.\n\n## Drawbacks\n\n1. The separation into states increases the number of modules to deal with.\n   This may cause unreasonable overhead for trivial state machines that are\n   better implemented using nested conditional logic.\n2. State implementations typically look very similar, causing some degree of\n   code duplication. This is usually addressed in OO Languages by introducing\n   base classes for states. C-Language doesn't offer such concepts and\n   CFSM does not try to mimic such OO behavior in C-language.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnhjschulz%2Fcfsm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnhjschulz%2Fcfsm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnhjschulz%2Fcfsm/lists"}