I have created a state diagram to show the different transitions and states. I could not find many examples of the state pattern in C, so I have taken an example from a Java state pattern and tried to convert it to C.
Is there a way to create the state pattern in C? I'd like some tips or tricks in doing this better using the state pattern.
This is compiled under gcc (GCC) 4.7.2.
watch_state.h (interface)
#ifndef WATCH_STATE_H_INCLUDED
#define WATCH_STATE_H_INCLUDED
/* An incomplete type for the state representation itself */
typedef struct tag_watch_state watch_state_t;
/* Function pointer to the implementation of the interface */
typedef void (*event_start_func_f)(watch_state_t *sw);
typedef void (*event_stop_func_f)(watch_state_t *sw);
typedef void (*event_split_func_f)(watch_state_t *sw);
typedef enum states_tag states_e;
enum states_tag { STOPPED, STARTED, SPLIT };
struct tag_watch_state {
/* Events */
event_start_func_f start;
event_split_func_f split;
event_stop_func_f stop;
/* States */
states_e current_state;
int time;
};
void initialize(watch_state_t *state);
char* state_to_string(states_e state);
#endif /* WATCH_STATE_H_INCLUDED */
watch_state.c (implementation of interface)
#include <stdio.h>
#include "watch_state.h"
#include "started_state.h"
#include "stopped_state.h"
#include "split_state.h"
static void default_start(watch_state_t *state);
static void default_split(watch_state_t *state);
static void default_stop(watch_state_t *state);
void initialize(watch_state_t *state)
{
state->start = default_start;
state->split = default_split;
state->stop = default_stop;
state->current_state = STOPPED;
state->time = 0;
}
char* state_to_string(states_e state)
{
switch(state) {
case STOPPED:
return "STOPPED";
case STARTED:
return "STARTED";
case SPLIT:
return "SPLIT";
default:
return "UNKNOWN";
}
}
static void default_start(watch_state_t *state)
{
printf("[ %s ] State [ %s ] time [ %d ] start event isn't supported in the concrete state\n",
__func__, state_to_string(state->current_state), state->time);
}
static void default_split(watch_state_t *state)
{
printf("[ %s ] State [ %s ] time [ %d ] split event isn't supported in the concrete state\n",
__func__, state_to_string(state->current_state), state->time);
}
static void default_stop(watch_state_t *state)
{
printf("[ %s ] State [ %s ] time [ %d ] stop event isn't supported in the concrete state\n",
__func__, state_to_string(state->current_state), state->time);
}
started_state.h
#ifndef STARTED_STATE_H_INCLUDED
#define STARTED_STATE_H_INCLUDED
#include "watch_state.h"
void start(watch_state_t *state);
#endif /* STARTED_STATE_H_INCLUDED */
started_state.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "started_state.h"
#include "stopped_state.h"
#include "split_state.h"
void start(watch_state_t *state)
{
unsigned int secs = 5;
unsigned int i = 0;
/* Do some work here to simulate stop*/
for(i = 0; i < secs; i++) {
sleep(1);
printf("Starting the stopwatch ...%u\n", i);
}
/* Initialize with default implementation */
initialize(state);
/* Update state */
state->current_state = STARTED;
state->time = 0;
/* Next transition states */
state->stop = stop;
state->split = split;
printf("[ %s ] state [ %s ] time [ %d ]\n", __func__, state_to_string(state->current_state), state->time);
}
stopped_state.h
#ifndef STOPPED_STATE_H_INCLUDED
#define STOPPED_STATE_H_INCLUDED
#include "watch_state.h"
void stop(watch_state_t *state);
void start(watch_state_t *state);
void split(watch_state_t *state);
#endif /* STOPPED_STATE_H_INCLUDED */
stopped_state.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "stopped_state.h"
#include "started_state.h"
void stop(watch_state_t *state)
{
unsigned int secs = 3;
unsigned int i = 0;
/* Do some work here to stop*/
for(i = 0; i < secs; i++) {
sleep(1);
printf("Stopping the stopwatch ...%u\n", i);
}
/* Initialize with default implementation */
initialize(state);
/* update current state */
state->current_state = STOPPED;
state->time = 0;
/* Next transition state */
state->start = start;
printf("[ %s ] state [ %s ] time [ %d ]\n", __func__, state_to_string(state->current_state), state->time);
}
split_state.h
#ifndef SPLIT_STATE_H_INCLUDED
#define SPLIT_STATE_H_INCLUDED
#include "watch_state.h"
void split(watch_state_t *state);
#endif /* SPLIT_STATE_H_INCLUDED */
split_state.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "split_state.h"
#include "stopped_state.h"
void split(watch_state_t *state)
{
unsigned int secs = 5;
unsigned int i = 0;
/* Do some work here to stop*/
for(i = 0; i < secs; i++) {
sleep(1);
printf("Splitting the stopwatch ...%u\n", i);
}
/* Initialize with default implementation */
initialize(state);
state->current_state = SPLIT;
state->time = secs;
/* Next transition states */
state->stop = stop;
state->split = split;
printf("[ %s ] state [ %s ] time [ %d ]\n", __func__, state_to_string(state->current_state), state->time);
}
For context, I have this for testing the states:
client_sw.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "watch_state.h"
#include "started_state.h"
typedef struct tag_client_sw client_sw_t;
struct tag_client_sw {
watch_state_t state;
};
client_sw_t* sw_create(void)
{
client_sw_t *sw = malloc(sizeof *sw);
memset(sw, 0, sizeof *sw);
initialize(&sw->state);
/* Next transition */
sw->state.start = start;
return sw;
}
void sw_destroy(client_sw_t *sw)
{
free(sw);
}
void start_watch(client_sw_t *sw)
{
sw->state.start(&sw->state);
}
void stop_watch(client_sw_t *sw)
{
sw->state.stop(&sw->state);
}
void split_watch(client_sw_t *sw)
{
sw->state.split(&sw->state);
}
int main(void)
{
client_sw_t *sw = NULL;
printf("Start your stop watch, extended version\n");
sw = sw_create();
stop_watch(sw);
stop_watch(sw);
split_watch(sw);
start_watch(sw);
start_watch(sw);
split_watch(sw);
split_watch(sw);
stop_watch(sw);
split_watch(sw);
start_watch(sw);
stop_watch(sw);
sw_destroy(sw);
printf("Terminate your stop watch\n");
return 0;
}