Implementation of the State Machine
Original link address: http://drdobbs.com/cpp/184401236? Pgno = 1
It is easy to implement a state machine, but it is not easy to implement a good state machine. Generally, the following implementation occurs when the state machine is implemented:Code:
Switch (State _)
Case
A:
Do_a ();
Case
B:
Do_ B ();
End Switch
This method is understandable when the number of states is small and the logic for changing between States is relatively simple, but it has the following Disadvantages:
L logic code is chaotic. For example, if status A is switched to status B, the code will become bloated and no longer intuitive if verification is required. Example:
Case:
If
(Current_state! = C)
Return
-1;
Else
Current_state
=;
Return
0;
Case ....:
....
L difficult to expand; most States are processed Similarly, while some special States need special processing. For example, additional data needs to be provided, for example, setting a State to suspend in a task, you need to pass a time to be suspended. This is similar to the GUI.ProgramEvent Notification interface in, such:
Handle_event (eventid Event _, long Ext ,...)
EXT can actually pass anything. For example, if the event dropopen triggers a file drag to the icon, you can pass in the address of the file path to open through Ext. This method is quite powerful, so we can fully learn from it when implementing the state machine.
Context:
Assume that a task is a state machine whose status changes.
L after a task is created, assume that the required resources are obtained and the task enters the ready status.
L The ready status can be run by the task queue,
Then the task enters the running state.
L The ready status can be suspended by suspend. During the suspension, you need to identify the suspension time.
L running status can be suspended
L The sushortded status can be run to bring the task into the running status.
L The running, ready, and sushortded statuses can all be directly entered into the ended status through cancel.
Question:
N reasonable switching between States
N facilitates expansion, the task status may increase, and the task trigger Time may change. The state machine must be able to quickly adapt to logical changes.
Solution:
The following solutions are discussed:
U design base class:
- The first is the virtual class used to transmit extended data.
# Ifndef event_data_h
# Define event_data_h
Class eventdata
{
Public:
Virtual ~ Eventdata (){};
Void * Data () = 0;
};
# Endif // event_data_h
- Statemachine
This class not only defines the interface, but also specifies the template for implementing the state machine. any implementation of the state machine can follow this template step by step.
# Ifndef state_machine_h
# Define state_machine_h
# Include <stdio. h>
# Include "eventdata. H"
Struct statestruct;
// Base class for state machines
Class statemachine
{
Public:
Statemachine (INT maxstates );
Virtual ~ Statemachine (){}
Protected:
Enum {event_ignored = 0xfe, cannot_happen };
Unsigned
Char currentstate;
Void externalevent (unsigned char, eventdata * = NULL );
Void internalevent (unsigned char, eventdata * = NULL );
Virtual const statestruct * getstatemap () = 0;
PRIVATE:
Const int _ maxstates;
Bool _ eventgenerated;
Eventdata * _ peventdata;
Void stateengine (void );
};
Typedef void
(Statemachine: * statefunc) (eventdata *);
Struct statestruct
{
Statefunc pstatefunc;
};
# Define begin_state_map \
Public :\
Const statestruct * getstatemap (){\
Static const statestruct statemap [] = {
# Define state_map_entry (entry )\
{
Reinterpret_cast <statefunc> (entry )},
# Define end_state_map \
{
Reinterpret_cast <statefunc> (null )}\
};\
Return & statemap [0];}
# Define begin_transition_map \
Static const unsigned char transitions [] = {\
# Define transition_map_entry (entry )\
Entry,
# Define end_transition_map (data )\
0
};\
Externalevent (transitions [currentstate], data );
# Endif // state_machine_h
Externalevent interface is an interface with validity verification. It first judges the validity of the status. If it is valid, internalevent is called,
Internalevent is an internal interface without verification, and it directly modifies the status.
- Statemachine implementation; this implementation is a general logical template, any state machine implementation can apply this template.
# Include <assert. h>
# Include "statemachine. H"
Statemachine: statemachine (INT maxstates ):
_ Maxstates (maxstates ),
Currentstate (0 ),
_ Eventgenerated (false ),
_ Peventdata (null)
{
}
// Generates an external event. Called once
Per external event
// To start the state machine executing
Void statemachine: externalevent (unsigned
Char newstate,
Eventdata *
Pdata)
{
// If we are supposed to ignore this event
If (newstate = event_ignored ){
// Just delete the event data, if any
If (pdata)
Delete pdata;
}
Else if (newstate = cannot_happen ){
//! Throw exception ("XXX ");
//! Or
//! Logerror ("....");
}
Else {
// Generate the event and execute the state engine
Internalevent (newstate, pdata );
Stateengine ();
}
}
// Generates an internal event. Called from
Within a state
// Function to transition to a new state
Void statemachine: internalevent (unsigned
Char newstate,
Eventdata *
Pdata)
{
_ Peventdata = pdata;
_ Eventgenerated = true;
Currentstate = newstate;
}
// The state engine executes the state
Machine states
Void statemachine: stateengine (void)
{
Eventdata * pdatatemp = NULL;
If (_ eventgenerated ){
Pdatatemp = _ peventdata; // copy
Of Event Data Pointer
_ Peventdata = NULL; // event
Data used up, reset PTR
_ Eventgenerated = false; // event
Used up, reset flag
Assert (currentstate <_ maxstates );
// Execute the state passing in event data, if any
Const statestruct * pstatemap = getstatemap ();
(This-> * pstatemap [currentstate]. pstatefunc) (pdatatemp );
// If event data was used, then delete it
If (pdatatemp ){
Delete pdatatemp;
Pdatatemp = NULL;
}
}
}
Externalevent checks whether the status is valid. If it is event_ignored, ignore this operation directly. If it is cannot_happen, a logical error occurs.
L the implementation of specific tasks is as follows:
# Ifndef task_h
# Define task_h
# Include "statemachine. H"
Struct taskdata: Public eventdata
{
Int xxx;
};
Class task: Public statemachine
{
Public:
Task (): statemachine (st_max_states ){}
// External events taken by this State Machine
Void suspend ();
Void run ();
Void cancel ();
PRIVATE:
// State machine state functions
Void st_ready ();
Void st_running ();
Void st_suincluded (taskdata * pdata );
Void st_ended ();
// State map to define State function order
Begin_state_map
State_map_entry (st_ready)
State_map_entry (st_running)
State_map_entry (st_suincluded)
State_map_entry (st_ended)
End_state_map
// State enumeration order must match the order of State
// Method entries in the state map
Enum e_states {
St_ready = 0,
St_running,
St_suincluded,
St_ended,
St_max_states
};
};
# Endif // motor_h
Begin_state_map macro registers the custom state function to statemap, so that the corresponding state function can be obtained through the state value index.
L task implementation code
# Include <assert. h>
# Include "task. H"
Void task: suspend (motordata * pdata)
{
Begin_transition_map
//-Current state-
Transition_map_entry (st_susponded)
// St_ready
Transition_map_entry (st_susponded)
// St_running
Transition_map_entry (event_ignored)
// St_susponded
Transition_map_entry (cannot_happen)
// St_ended
End_transition_map (pdata)
}
Void task: Run (void)
{
Begin_transition_map
//-Current state-
Transition_map_entry (st_running)
// St_ready
Transition_map_entry (event_ignored)
// St_running
Transition_map_entry (st_running)
// St_susponded
Transition_map_entry (cannot_happen)
// St_ended
End_transition_map (null)
}
Void task: Cancel (void)
{
Begin_transition_map
//-Current state-
Transition_map_entry (st_ended)
// St_ready
Transition_map_entry (st_ended)
// St_running
Transition_map_entry (st_ended)
// St_susponded
Transition_map_entry (event_ignored) // st_ended
End_transition_map (null)
}
Void task: st_ready ()
{
Internalevent (st_ready );
}
Void task: st_running ()
{
Internalevent (st_running );
}
Void task: st_suincluded (motordata * pdata)
{
Internalevent (st_suincluded, pdata );
}
Void task: st_ended ()
{
Internalevent (st_ended );
}
In terms of status processing, the State is either valid, negligible, or not occurring at all.