Skip to content

Instantly share code, notes, and snippets.

@christophevg
Last active March 16, 2016 13:22
Show Gist options
  • Save christophevg/1f1316f11c0643ba5224 to your computer and use it in GitHub Desktop.
Save christophevg/1f1316f11c0643ba5224 to your computer and use it in GitHub Desktop.
State Machines for Dries

State Machines for Dries

Three implementations:

  • demo_switch.c most basic, switch-based state machine
  • demo_functions.c a little nicer on the eye with functions as state handlers
  • demo_functions_results.c a more generic state machine framework with reconfigurable state transitions

Normal operation

$ gcc demo_switch.c && ./a.out 
(0)> s
(1)> w
(1)> w
(1)> w
nice work
we're done

Abnormal operation

$ gcc demo_switch.c && ./a.out 
(0)> 1
(0)> 2
(0)> 3
(0)> s
(1)> w
(1)> w
(1)> X
whoops (X)
we're done
#include <stdio.h>
#include <stdbool.h>
enum states {
IDLE,
WORK,
SUCCESS,
ERROR,
END
} current_state = IDLE;
typedef void(*state_t)(void);
char input = ' ';
int work = 0;
bool active = true;
void handle_idle(void) {
// wait for 's' command to start work
if(input == 's') {
current_state = WORK;
}
}
void handle_work(void) {
// accept 3 'w' characters while working
if(input == 'w') {
work++;
if(work >= 3) {
current_state = SUCCESS;
}
} else {
// anything else is errornous
current_state = ERROR;
}
}
void handle_success(void) {
printf("nice work\n");
current_state = END;
}
void handle_error(void) {
printf("whoops (%c)\n", input);
current_state = END;
}
void handle_end(void) {
// when done, we return to exit the initial call, stopping the loop
printf("we're done\n");
active = false;
}
state_t states[] = {
/* IDLE */ handle_idle,
/* WORK */ handle_work,
/* SUCCESS */ handle_success,
/* ERROR */ handle_error,
/* END */ handle_end
};
void get_input(void) {
// get input from user when IDLE or WORKing
if(current_state <= WORK) {
printf("(%d)> ", current_state);
input = fgetc(stdin);
fgetc(stdin); // Enter
}
}
void do_some_work() {
while(active) {
get_input();
states[current_state]();
}
}
int main(void) {
do_some_work();
}
#include <stdio.h>
#include <stdbool.h>
typedef enum state {
IDLE,
WORK,
SUCCESS,
ERROR,
END,
_
} state_t;
typedef enum result {
CONTINUE,
NEXT,
FAIL,
} result_t;
typedef result_t(*state_handler_t)(void);
typedef struct transition {
state_handler_t handler;
state_t results[3];
} transition_t;
state_t current_state = IDLE;
char input = ' ';
int work = 0;
result_t handle_idle(void) {
// wait for 's' command to start work
if(input == 's') {
return NEXT;
}
return CONTINUE;
}
result_t handle_work(void) {
// accept 3 'w' characters while working
if(input == 'w') {
work++;
if(work >= 3) {
return NEXT;
}
} else {
// anything else is errornous
return FAIL;
}
return CONTINUE;
}
result_t handle_success(void) {
printf("nice work\n");
return NEXT;
}
result_t handle_error(void) {
printf("whoops (%c)\n", input);
return NEXT;
}
result_t handle_end(void) {
// when done, we return to exit the initial call, stopping the loop
printf("we're done\n");
return NEXT;
}
transition_t transitions[] = {
/* STATE HANDLER CONTINUE NEXT FAIL */
{ /* IDLE */ handle_idle, { IDLE, WORK, _ }},
{ /* WORK */ handle_work, { WORK, SUCCESS, ERROR }},
{ /* SUCCESS */ handle_success, { _, END, _ }},
{ /* ERROR */ handle_error, { _, END, _ }},
{ /* END */ handle_end, { _, _, _ }},
};
void get_input(void) {
// get input from user when IDLE or WORKing
if(current_state <= WORK) {
printf("(%d)> ", current_state);
input = fgetc(stdin);
fgetc(stdin); // Enter
}
}
void do_some_work() {
while(true) {
get_input();
result_t result = transitions[current_state].handler();
current_state = transitions[current_state].results[result];
if(current_state == _) { return; }
}
}
int main(void) {
do_some_work();
}
#include <stdio.h>
#include <stdbool.h>
enum states {
IDLE,
WORK,
SUCCESS,
ERROR,
END
} current_state = IDLE;
char input = ' ';
int work = 0;
void get_input(void) {
// get input from user when IDLE or WORKing
if(current_state <= WORK) {
printf("(%d)> ", current_state);
input = fgetc(stdin);
fgetc(stdin); // Enter
}
}
bool handle_current_state(void) {
switch(current_state) {
case IDLE:
// wait for 's' command to start work
if(input == 's') {
current_state = WORK;
}
break;
case WORK:
// accept 3 'w' characters while working
if(input == 'w') {
work++;
if(work >= 3) {
current_state = SUCCESS;
}
} else {
// anything else is errornous
current_state = ERROR;
}
break;
case SUCCESS:
printf("nice work\n");
current_state = END;
break;
case ERROR:
printf("whoops (%c)\n", input);
current_state = END;
break;
case END:
// when done, we return to exit the initial call, stopping the loop
printf("we're done\n");
return false;
break;
}
get_input();
return true;
}
void do_some_work() {
bool carry_on = true;
while(carry_on) {
carry_on = handle_current_state();
}
}
int main(void) {
do_some_work();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment