Using C + + to implement a simple state machine model--principle analysis

Source: Internet
Author: User

In the previous article, we described how this state machine model is used. By example, we find that we can use this model to quickly build a state machine that meets basic business needs. In this paper, we will parse the basic code of the model so that you can modify it according to the characteristics of your state machine. (reprint please indicate CSDN blog for breaksoftware)

The base method of the template library is implemented in the AutoStateChart.h of the project given later, which has a total of 215 rows, of which 16 lines are auxiliary debugging code. In the above article, the state machine class is as an example:

Class Cmachine_download_run_app: Public    Autostatechart::cautostatechartmachine<cmachine_download_run_app, Cstoreofmachine>
The Cmachine_download_run_app class inherits from the template class Cautostatechartmachine, which has two parameters: inheriting the class itself and cstoreofmachine. The Cstoreofmachine class, as its name implies, is the class used in the state machine to store data. Why do you design such a class? Because in our state machine model, each of the underlying states is fragmented. One of the benefits of this design is that we can make the logic code independent of each underlying state, without any coupling to the other modules. When we want to delete a state, we simply remove it from the state machine's jump declaration. When we want to add a new state, we just have to make a declaration in the state machine jump. But often the advantages are accompanied by drawbacks: it causes the data interaction of each underlying state class to be an obstacle. In particular, there is no context for the underlying state, and the leaping information will become very difficult. So we need a "database" that survives the entire state machine declaration cycle, which can be accessed and modified by each of the underlying state classes. So Cstoreofmachine was born. Because the class is more independent, we start with the class parsing. First we look at the declaration of the 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 (get = get# #name, put = set# #name)) type prop# #name;p rivate:type m_# #name; class cstoreofmachine{ Property (std::string, valuestring); Property (std::wstring, valuewstring);    Property (int, valueint);};
This class may be written only for the Windows VS platform, which is not demonstrated by other platforms. In fact, its content is very simple, is to expose the member variable of the set and get method. But I think this kind of writing is more interesting, just listed here.
Let's take a look at the use of the class in the template, we'll start with the most basic class.

Class Cempytlocalstore{};template<class Store = Cempytlocalstore>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 user of the template does not need a "database", it is possible to declare the "database" class in the template, at which point our cemptylocalstore is in effect. For example, the state machine of our previous example can be changed to:

Class Cmachine_download_run_app: Public    Autostatechart::cautostatechartmachine<cmachine_download_run_app >
The Clocalstoreaccess class mainly provides the following functions:

    1. To set methods to access the database class object--setstore
    2. Gets the database class object--getstore
The member variable m_pfunc is a function pointer that gets the database class object. The variable will be set by the Cloaclstoreaccess inheritance class, which is equivalent to clocalstoreaccess exposing the ability to set access to the database class object. It does not save the "database" class object-it only provides "access" capability, not "storage" capability.
Template<class Store = Cempytlocalstore>class clocalstorebase:public boost::enable_shared_from_this< Clocalstorebase<store>>,public clocalstoreaccess<store> {public:void Init () {func Pfunc = Boost::bind ( &clocalstorebase<store>::_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, meaning that the class provides "storage" functionality. It inherits from the Cloaclstoreaccess class, which gives the class the ability to access the database-although its private methods can access the "Database" class object, but I still want to separate these capabilities. Because the underlying state class described later should have the ability to "access", rather than the ability to "store" it. If you do not split these capabilities, you will cause hierarchy confusion.
The Init method of the Cloaclstorebase class, which opened up the relationship with clocalstoreaccess-set the function pointer.
After describing the template classes used to store the context, we can now focus on the state machine-related classes. Let's take a look at an example of a basic state class in the article
Class Csimplestate_download_from_a: Public    Autostatechart::cautostatechartbase<csimplestate_download_from _a, Cmachine_download_run_app, cstoreofmachine>
The Csimplestate_download_from_a class inherits from the Cautostatechartbase template class. The first template parameter is the inheriting class itself, the second is the state machine to which it belongs, and the third is the "Database" class. We're looking at the declaration of the Cautostatechartbase class.

Template<class T, Class machineorcompositestates, class Store = Cempytlocalstore>class cautostatechartbase: Public Boost::enable_shared_from_this<cautostatechartbase<t,machineorcompositestates,store>>,public Clocalstoreaccess<store>{boost_typeof_register_type (t) public:std::string GetCurrentState () {return typeid (t ). Name ();}; BOOL Iscompositestates () {return false;}; void Setinitstate (const std::string& strstate) {};p ublic:virtual void Entry () {}; Virtual std::string Exit () {return "";};};
The template class uses the class of the first template parameter class to inherit the state of the class, and uses the Getcurrentstate method to provide the fetch functionality. For example, the status in the previous example is named Class Csimplestate_download_from_a. This template class inherits from the Clocalstoreaccess template class, enabling the inheriting class to have the ability to "access" the third template parameter class-the "database" class-without the "storage" capability. The class also exposes two methods,--entry and exit, that are used to let the state machine invoke when in and out of the state.
State and storage classes are all done, and we have the state machine class and the composite state class that are scheduled to change state. In fact, in a way, the composite state is a simple state machine, and they are common in many places. We explain it from the state machine class entrance. First look at the example in the previous article
Class Cmachine_download_run_app: Public    Autostatechart::cautostatechartmachine<cmachine_download_run_app, Cstoreofmachine>
The State machine class needs to inherit from the Cautostatechartmachine template class, which declares the following:
template< Class T, class Localstore = Cempytlocalstore>class cautostatechartmachine:public boost::enable_shared_from_this <cautostatechartmachine<t,localstore>>,public Clocalstoreaccess<localstore>{public:typedef Localstore selfstore;typedef T self;public:cautostatechartmachine () {M_spstore.reset ();}; Virtual ~cautostatechartmachine () {};p rivate:virtual bool Transition () {return false;}; Public:void Startmachine () {if (!m_spstore) {M_spstore = boost::make_shared<clocalstorebase<localstore>> (); M_spstore->init (); Setstore (M_spstore->m_pfunc);} while (Transition ()) {};}; Private:void Init () {};p Ublic:bool iscompositestates () {return false;}; Protected:std::string m_strcurrentstate;std::string m_strcondition; Mapstring m_mapcompositestatessubstate;boost::shared_ptr<clocalstorebase<localstore>> M_spStore;}; 
Let's look at the member variables of this class first. M_strcurrentstate saves the state machine's current state in the jump and m_strcondition the output of the state before the current state in the state machine, which is used to determine the direction of the state jump. The m_mapcompositestatessubstate is used to save the state machine's last state when it leaves the compound state, that is, it records the shallow 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 The first time it is invoked. Then the dead loop calls the transition method. 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 function.

Let's look at the underlying template for the composite state class.

Template<class T, Class machineorcompositestates, class Localstore = Cempytlocalstore>class CCompositeStates: Public Cautostatechartbase<t,machineorcompositestates,localstore>{boost_typeof_register_type (T) Public: Ccompositestates () {};~ccompositestates () {};p rivate: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 has to have entry and exit two ways. 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 methods inherited from the template class are also implemented transition.
So all the center of gravity is focused on the implementation of the transition method.
In order to make the code beautiful, I refer to MFC in the use of macro-concise code ideas, the following macro design:
#define STARTSTATE (state) do {boost::shared_ptr<state> sp = boost::make_shared<state> (); 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: "<<strinitstate<<std::endl;}}} Sp->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:" <<strState<< "" << strinnerstate << Std::endl;}}} while (0), #defineREGISTERSTATECONVERTBEGIN (startstate) bool Transition () {do {if (M_strcurrentstate.empty ()) {m_ StrcurrentState = typeID (startstate). Name (); Startstate (startstate); return true;}} while (0), #define REGISTERSTATECONVERT (fromstate,condition,tostate) do {std::string strfromstate = typeid (fromstate). Name (); std::string strtostate = typeID (tostate). Name (); if (Debugframeflag) {std::cout<< "Strfromstate:" < <strFromState<<std::endl;std::cout<< "Condition:" <<condition<<std::endl;std::cout << "Strtostate:" <<strToState<<std::endl;std::cout<< "m_strcurrentstate:" <<m_ strcurrentstate<<std::endl;std::cout<< "M_strcondition:" <<m_strCondition<<std::endl< <std::endl;} if (Iscompositestates ()) {if (strfromstate! = m_strcurrentstate| | (!m_strcondition.empty () && Condition! = m_strcondition)) {break;}} else {if (strfromstate! = m_strcurrentstate| | Condition! = m_strcondition) {break;}} M_strcurrentstate = strtostate; Startstate (tostate); return true;} while (0); #define Registerstateconvertend () return false;};
Then the composite State class and the state machine class can clearly describe the process as long as the macros are used to organize the state jumps.

Using C + + to implement a simple state machine model--principle analysis

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.