Using C ++ to implement a simple state machine model -- Principle Analysis
In the previous article, we introduced how to use the state machine model. Through examples, we can use this model to quickly build a state machine that meets basic business needs. In this article, we will parse the basic code of this model so that you can modify it according to the characteristics of your own state machine. (For details, refer to the csdn blog for breaksoftware)
The basic method of the template library is implemented in the AutoStateChart. h of the project given later. There are a total of 215 lines in the file, of which 16 lines are auxiliary debugging code. The state machine class in the preceding article is used as an example:
class CMachine_Download_Run_App : public AutoStateChart::CAutoStateChartMachine
The CMachine_Download_Run_App class inherits from the template class CAutoStateChartMachine. The template class has two parameters: the inheritance class itself and CStoreofMachine. The CStoreofMachine class, as its name implies, is used to store data in a state machine. Why design such a class? In our state machine model, each basic state is separated. One benefit of this design is that we can separate the logic code of each basic State without any coupling with other modules. To delete a state, we only need to remove it from the jump Declaration of the state machine. When we want to add a new state, we only need to make the corresponding declaration in the state machine jump. However, the advantage is also accompanied by the disadvantage: it makes the data interaction of each basic state class an obstacle. In particular, if there is no context, it is very difficult to transfer information. Therefore, we need a "Database" that exists throughout the state machine declaration cycle and can be accessed and modified by each basic state class. Therefore, CStoreofMachine came into being. Because this class is relatively independent, we start to parse it from this class. First, let's look at the Declaration of this class:
#pragma once#include "AutoStateChart.h"#define PROPERTY(type,name)\public:\void Set##name(const type& n) { \m_##name = n;\}\type Get##name() {\return m_##name;\}\__declspec(property(get = Get##name, put = Set##name)) type Prop##name;\private:\type m_##name;\class CStoreofMachine{PROPERTY(std::string, ValueString);PROPERTY(std::wstring, ValueWString); PROPERTY(int, ValueInt);};This type of writing method may only be suitable for windows vsplatforms. It has not been demonstrated by other platforms. In fact, its content is very simple, that is, the set and get methods that expose member variables. I just want to list this topic here.
Let's take a look at the usage of this class in the template. We will first parse it from the most basic class.
class CEmpytLocalStore{};template
class CLocalStoreAccess{public:typedef boost::function< Store& () > func;Store& GetStore(){return m_pFunc();};void SetStore(func& pCallback){m_pFunc = pCallback;};public:func m_pFunc;};
We first define an empty class-CEmptyLocalStore, which is equivalent to a default "Database ". When the template user does not need a "Database", the "Database" class can be declared in the template. In this case, our CEmptyLocalStore takes effect. For example, the state machine in the above example can be changed:
class CMachine_Download_Run_App : public AutoStateChart::CAutoStateChartMachine
The CLocalStoreAccess class provides the following functions:
Set the method for accessing the "Database" Class Object -- SetStore gets the "Database" Class Object -- GetStore member variable m_pFunc is a function pointer used to obtain the "Database" Class Object. This variable will be set by the CLoaclStoreAccess inheritance class, which is equivalent to CLocalStoreAccess exposing the ability to set access to "Database" class objects. But it does not store "Database" class objects-it only provides "access" capabilities, rather than "Storage" capabilities.
template
class CLocalStoreBase:public boost::enable_shared_from_this
>,public CLocalStoreAccess
{public:void Init(){ func pfunc = boost::bind(&CLocalStoreBase
::_GetStore, shared_from_this()); SetStore(pfunc);};private:Store& _GetStore(){return m_Store;};private:Store m_Store;};
The private member variable m_Store of the CLoaclStoreBase class is the "Database" class object, which provides the "Storage" function. It inherits from the CLoaclStoreAccess class so that the class can access the database-although its private method can access the "Database" class object, I still want to separate these capabilities. This is because the basic status class described later must have the "access" capability, rather than the "Storage" capability. If you do not split these capabilities, the hierarchy will be disordered.
The init method of the CLoaclStoreBase class opens up the relationship with ClocalStoreAccess-setting function pointers.
After introducing the template class used to store the context, we can now focus on the classes related to the state machine. Let's first look at the example of a basic state class in the previous article.
class CSimpleState_Download_From_A : public AutoStateChart::CAutoStateChartBase
The CSimpleState_Download_From_A class inherits from the CAutoStateChartBase template class. The first template parameter is the inheritance class itself, the second is the state machine to which it belongs, and the third is the database class. Let's take a look at the CAutoStateChartBase class declaration.
template
class CAutoStateChartBase:public boost::enable_shared_from_this
>,public CLocalStoreAccess
{BOOST_TYPEOF_REGISTER_TYPE(T)public:std::string GetCurrentState(){ return typeid(T).name();};bool IsCompositeStates(){return false;};void SetInitState( const std::string& strState ){};public:virtual void Entry(){}; virtual std::string Exit(){return "";};};
This template class uses the Class Name of the first template parameter class as the status of its inherited class, and uses the GetCurrentState method to obtain the function. For example, in the preceding example, the status is class CSimpleState_Download_From_A. This template class inherits from the CLocalStoreAccess template class, so that the inheritance class has the ability to "access" the third template parameter class-"Database" Class-not capable of "Storage. At the same time, this class exposes two methods, Entry and Exit, which are used to call the state machine when entering or exiting the state.
After both the status and storage classes are introduced, the state machine and composite status classes with changed scheduling statuses are left. In fact, to some extent, a compound state is a simple state machine, which has commonalities in many places. We will explain from the entry of the state machine class. First, let's take a look at the example in the previous article.
class CMachine_Download_Run_App : public AutoStateChart::CAutoStateChartMachine
The state machine class must inherit from the CAutoStateChartMachine template class. The class declaration is as follows:
template
class CAutoStateChartMachine:public boost::enable_shared_from_this
>,public CLocalStoreAccess
{public:typedef LocalStore SelfStore;typedef T Self;public:CAutoStateChartMachine(){m_spStore.reset();};virtual ~CAutoStateChartMachine(){};private:virtual bool Transition(){return false;};public:void StartMachine() {if ( !m_spStore ) {m_spStore = boost::make_shared
>();m_spStore->Init();SetStore( m_spStore->m_pFunc );}while( Transition()){};};private:void Init(){};public:bool IsCompositeStates(){return false;};protected:std::string m_strCurrentState;std::string m_strCondition;MapString m_MapCompositeStatesSubState;boost::shared_ptr
> m_spStore;};
Let's take a look at the member variables of this class. M_strCurrentState stores the current state of the state machine in the jump, m_strCondition stores the output of the State before the current state in the state machine, and is used to determine the status jump direction. M_MapCompositeStatesSubState is used to save the final state when the state machine leaves the composite state, that is, it records the shortest history of the composite state machine. M_spStore points to the "Database" Class Object.
The most important function of the template class is StartMachine, which creates a "Database" class object when it is called for the first time. Call the Transition method in an endless loop. The Tansition method is a virtual method, which is the core of the entire model. The state machine class needs to implement its own Transition method so that the state machine can run.
Let's look at the basic template of the composite status class.
template
class CCompositeStates:public CAutoStateChartBase
{BOOST_TYPEOF_REGISTER_TYPE(T)public:CCompositeStates(){};~CCompositeStates(){};private:virtual bool Transition(){return false;};public:virtual void Entry(){while(Transition());};virtual std::string Exit(){return m_strCondition;};public:std::string GetCurrentState(){return m_strCurrentState;};bool IsCompositeStates(){return true;};void SetInitState( const std::string& strState ){ m_strCurrentState = strState; };protected:std::string m_strCurrentState;std::string m_strCondition;MapString m_MapCompositeStatesSubState;};
Because the compound State is also a state, it also requires two methods: Entry and Exit. The Entry method is to call the Transition method. The Transition method of the template class is also a virtual method, which means that the method inherited from the template class also needs to implement Transition.
So all the centers of gravity are focused on the implementation of the Transition method.
To make the Code look beautiful, I have designed the following macros by referring to the idea of using the macro concise code in MFC:
# Define STARTSTATE (state) \ do {\ boost: shared_ptr
Sp = boost: make_shared
(); \ Sp-> SetStore (m_pFunc); \ if (sp-> IsCompositeStates () {\ std: string strState = typeid (state ). name (); \ BOOST_AUTO (it, m_MapCompositeStatesSubState.find (strState); \ if (m_MapCompositeStatesSubState.end ()! = It) {\ sp-> SetInitState (it-> second); \ if (DEBUGFRAMEFLAG) {\ std: string strInitState = it-> second; \ std :: cout <"CompositeStates SetInitState:" <
Entry (); \ m_strCondition = sp-> Exit (); \ if (sp-> IsCompositeStates () {\ std: string strState = typeid (state ). name (); \ std: string strInnerState = sp-> GetCurrentState (); \ m_MapCompositeStatesSubState [strState] = strInnerState; \ if (DEBUGFRAMEFLAG) {\ std :: cout <"CompositeStates SaveState:" <
Then the composite state class and state machine class can clearly describe the process as long as they use these macros to organize State jumps.