_smach.h

Go to the documentation of this file.
00001 
00022 /* === S T A R T =========================================================== */
00023 
00024 #ifndef __ETL__SMACH_H_
00025 #define __ETL__SMACH_H_
00026 
00027 /* === H E A D E R S ======================================================= */
00028 
00029 #include <vector>
00030 #include <algorithm>
00031 #include <stdexcept>
00032 #include "_mutex_null.h"
00033 #include "_misc.h"
00034 
00035 /* === M A C R O S ========================================================= */
00036 
00037 #define SMACH_STATE_STACK_SIZE      (32)
00038 
00039 #ifdef _MSC_VER
00040 #pragma warning (disable:4786)
00041 #pragma warning (disable:4290) // MSVC6 doesnt like function declarations with exception specs
00042 #endif
00043 
00044 //#define ETL_MUTEX_LOCK()      _mutex::lock lock(mutex)
00045 #define ETL_MUTEX_LOCK()
00046 
00047 /* === T Y P E D E F S ===================================================== */
00048 
00049 /* === C L A S S E S & S T R U C T S ======================================= */
00050 
00051 _ETL_BEGIN_NAMESPACE
00052 
00059 template <typename CON, typename K=int, typename M=mutex_null>
00060 class smach
00061 {
00062 public:
00063 
00064     typedef K event_key;
00065     typedef M _mutex;
00066     typedef CON context_type;
00067 
00068 
00069     struct egress_exception { };
00070     struct pop_exception { };
00071 
00072 
00074     enum event_result
00075     {
00076         // These values are returned by the event
00077         // handlers cast to state pointers.
00078         RESULT_ERROR,       
00079         RESULT_OK,          
00080         RESULT_ACCEPT,      
00081         RESULT_REJECT,      
00082 
00083         RESULT_END          
00084     };
00085 
00086     //template<typename T> class state;
00087 
00089     struct event
00090     {
00091         event_key key;
00092 
00093         event() { }
00094         event(const event_key& key):key(key) { }
00095 
00096         operator event_key()const { return key; }
00097     };
00098 
00100     template<typename T>
00101     class event_def
00102     {
00103         // List our friends
00104         friend class smach;
00105         //friend class state<T>;
00106 
00107     public:
00108         typedef T state_context_type;
00109 
00111         typedef event_result (T::*funcptr)(const event&);
00112 
00113     //private:
00114 
00115         event_key id;       //<! Event ID
00116         funcptr handler;    //<! Pointer event handler
00117 
00118     public:
00119 
00121         bool operator<(const event_def &rhs)const
00122             { return id<rhs.id; }
00123 
00125         bool operator==(const event_def &rhs)const
00126             { return id==rhs.id; }
00127 
00129         bool operator<(const event_key &rhs)const
00130             { return id<rhs; }
00131 
00133         bool operator==(const event_key &rhs)const
00134             { return id==rhs; }
00135 
00137         event_def() { }
00138 
00140         event_def(event_key a, funcptr b):id(a),handler(b) { }
00141 
00143         event_def(const event_def &x):id(x.id),handler(x.handler) { }
00144 
00145     };
00146 
00147     class state_base
00148     {
00149         // Our parent is our friend
00150         friend class smach;
00151     public:
00152         virtual ~state_base() { }
00153 
00154         virtual void* enter_state(context_type* machine_context)const=0;
00155 
00156         virtual bool leave_state(void* state_context)const=0;
00157 
00158         virtual event_result process_event(void* state_context,const event& id)const=0;
00159 
00160         virtual const char *get_name() const=0;
00161     };
00162 
00164     template<typename T>
00165     class state : public state_base
00166     {
00167         // Our parent is our friend
00168         friend class smach;
00169 
00170     public:
00171         typedef event_def<T> event_def;
00172         typedef T state_context_type;
00173 
00174 
00175     private:
00176 
00177         std::vector<event_def> event_list;
00178 
00179         smach *nested;      
00180         event_key low,high; 
00181         const char *name;   
00182         typename event_def::funcptr default_handler;    
00183 
00184     public:
00185 
00187         state(const char *n, smach* nest=0):
00188             nested(nest),name(n),default_handler(NULL)
00189         { }
00190 
00191         virtual ~state() { }
00192 
00194 
00195         void set_nested_machine(smach *sm) { nested=sm; }
00196 
00198         void set_default_handler(const typename event_def::funcptr &x) { default_handler=x; }
00199 
00201         virtual const char *get_name() const { return name; }
00202 
00203         state_context_type& get_context(smach& machine)
00204         {
00205             state_context_type *context(dynamic_cast<state_context_type*>(machine.state_context));
00206             if(context)
00207                 return context;
00208 
00209         }
00210 
00212         void
00213         insert(const event_def &x)
00214         {
00215             // If this is our first event_def,
00216             // setup the high and low values.
00217             if(!event_list.size())
00218                 low=high=x.id;
00219 
00220             // Sort the event_def onto the list
00221             event_list.push_back(x);
00222             sort(event_list.begin(),event_list.end());
00223 
00224             // Update the low and high markers
00225             if(x.id<low)
00226                 low=x.id;
00227             if(high<x.id)
00228                 high=x.id;
00229         }
00230 
00231         typename std::vector<event_def>::iterator find(const event_key &x) { return binary_find(event_list.begin(),event_list.end(),x); }
00232         typename std::vector<event_def>::const_iterator find(const event_key &x)const { return binary_find(event_list.begin(),event_list.end(),x); }
00233 
00234     protected:
00235 
00236         virtual void* enter_state(context_type* machine_context)const
00237         {
00238             return new state_context_type(machine_context);
00239         }
00240 
00241         virtual bool leave_state(void* x)const
00242         {
00243             state_context_type* state_context(reinterpret_cast<state_context_type*>(x));
00244             delete state_context;
00245             return true;
00246         }
00247 
00248         virtual event_result
00249         process_event(void* x,const event& id)const
00250         {
00251             state_context_type* state_context(reinterpret_cast<state_context_type*>(x));
00252 
00253             // Check for nested machine in state
00254             if(nested)
00255             {
00256                 const event_result ret(nested->process_event(id));
00257                 if(ret!=RESULT_OK)
00258                     return ret;
00259             }
00260 
00261             // Quick test to make sure that the
00262             // given event is in the state
00263             if(id.key<low || high<id.key)
00264                 return RESULT_OK;
00265 
00266             // Look for the event
00267             typename std::vector<event_def>::const_iterator iter(find(id.key));
00268 
00269             // If search results were negative, fail.
00270             if(iter->id!=id.key)
00271                 return RESULT_OK;
00272 
00273             // Execute event function
00274             event_result ret((state_context->*(iter->handler))(id));
00275 
00276             if(ret==RESULT_OK && default_handler)
00277                 ret=(state_context->*(default_handler))(id);
00278 
00279             return ret;
00280         }
00281     };
00282 
00283 private:
00284 
00285     // Machine data
00286     const state_base* curr_state;   
00287     smach* child;                  
00288 
00289 public: // this really should be private
00290     void* state_context;        
00291 private:
00292 
00293     context_type* machine_context;      
00294 
00295     const state_base* default_state;
00296     void*   default_context;
00297 
00298 #ifdef ETL_MUTEX_LOCK
00299     _mutex mutex;
00300 #endif
00301 
00303     const state_base*   state_stack[SMACH_STATE_STACK_SIZE];
00304     void*               state_context_stack[SMACH_STATE_STACK_SIZE];
00305     int                 states_on_stack;
00306 
00307 public:
00308 
00310     const char *
00311     get_state_name()const
00312     {
00313 #ifdef ETL_MUTEX_LOCK
00314         ETL_MUTEX_LOCK();
00315 #endif
00316         if(curr_state)
00317             return curr_state->get_name();
00318         if(default_state)
00319             return default_state->get_name();
00320         return 0;
00321     }
00322 
00324 
00326     static bool
00327     event_error(const event_result &rhs)
00328         { return rhs<=RESULT_ERROR; }
00329 
00330     bool
00331     set_default_state(const state_base *nextstate)
00332     {
00333 #ifdef ETL_MUTEX_LOCK
00334         ETL_MUTEX_LOCK();
00335 #endif
00336         // Keep track of the current state unless
00337         // the state switch fails
00338         const state_base *prev_state=default_state;
00339 
00340         // If we are already in a state, leave it and
00341         // colapse the state stack
00342         if(default_state)
00343             default_state->leave_state(default_context);
00344 
00345         // Set this as our current state
00346         default_state=nextstate;
00347         default_context=0;
00348 
00349         // Attempt to enter the state
00350         if(default_state)
00351         {
00352             default_context=default_state->enter_state(machine_context);
00353             if(default_context)
00354                 return true;
00355         }
00356         else
00357             return true;
00358 
00359         // We failed, so attempt to return to previous state
00360         default_state=prev_state;
00361 
00362         // If we had a previous state, enter it
00363         if(default_state)
00364             default_context=default_state->enter_state(machine_context);
00365 
00366         // At this point we are not in the
00367         // requested state, so return failure
00368         return false;
00369     }
00370 
00372 
00373     bool
00374     egress()
00375     {
00376 #ifdef ETL_MUTEX_LOCK
00377         ETL_MUTEX_LOCK();
00378 #endif
00379 
00380         // Pop all states off the state stack
00381         while(states_on_stack) pop_state();
00382 
00383         // If we are not in a state, then I guess
00384         // we were successful.
00385         if(!curr_state)
00386             return true;
00387 
00388         // Grab the return value from the exit function
00389         bool ret=true;
00390 
00391         const state_base* old_state=curr_state;
00392         void *old_context=state_context;
00393 
00394         // Clear out the current state and its state_context
00395         curr_state=0;state_context=0;
00396 
00397         // Leave the state
00398         return old_state->leave_state(old_context);
00399 
00400         return ret;
00401     }
00402 
00404 
00407     bool
00408     enter(const state_base *nextstate)
00409     {
00410 #ifdef ETL_MUTEX_LOCK
00411         ETL_MUTEX_LOCK();
00412 #endif
00413 
00414         // Keep track of the current state unless
00415         // the state switch fails
00416         const state_base *prev_state=curr_state;
00417 
00418         // If we are already in a state, leave it and
00419         // colapse the state stack
00420         if(curr_state)
00421             egress();
00422 
00423         // Set this as our current state
00424         curr_state=nextstate;
00425         state_context=0;
00426 
00427         // Attempt to enter the state
00428         state_context=curr_state->enter_state(machine_context);
00429         if(state_context)
00430             return true;
00431 
00432         // We failed, so attempt to return to previous state
00433         curr_state=prev_state;
00434 
00435         // If we had a previous state, enter it
00436         if(curr_state)
00437             state_context=curr_state->enter_state(machine_context);
00438 
00439         // At this point we are not in the
00440         // requested state, so return failure
00441         return false;
00442     }
00443 
00445 
00450     bool
00451     push_state(const state_base *nextstate)
00452     {
00453 #ifdef ETL_MUTEX_LOCK
00454         ETL_MUTEX_LOCK();
00455 #endif
00456 
00457         // If there are not enough slots, then throw something.
00458         if(states_on_stack==SMACH_STATE_STACK_SIZE)
00459             throw(std::overflow_error("smach<>::push_state(): state stack overflow!"));
00460 
00461         // If there is no current state, nor anything on stack,
00462         // just go ahead and enter the given state.
00463         if(!curr_state && !states_on_stack)
00464             return enter(nextstate);
00465 
00466         // Push the current state onto the stack
00467         state_stack[states_on_stack]=curr_state;
00468         state_context_stack[states_on_stack++]=state_context;
00469 
00470         // Make the next state the current state
00471         curr_state=nextstate;
00472 
00473         // Try to enter the next state
00474         state_context=curr_state->enter_state(machine_context);
00475         if(state_context)
00476             return true;
00477 
00478         // Unable to push state, return to old one
00479         curr_state=state_stack[--states_on_stack];
00480         state_context=state_context_stack[states_on_stack];
00481         return false;
00482     }
00483 
00485 
00486     void
00487     pop_state()
00488     {
00489 #ifdef ETL_MUTEX_LOCK
00490         ETL_MUTEX_LOCK();
00491 #endif
00492 
00493         // If we aren't in a state, then there is nothing
00494         // to do.
00495         if(!curr_state)
00496             throw(std::underflow_error("smach<>::pop_state(): stack is empty!"));
00497 
00498         if(states_on_stack)
00499         {
00500             const state_base* old_state=curr_state;
00501             void *old_context=state_context;
00502 
00503             // Pop previous state off of stack
00504             --states_on_stack;
00505             curr_state=state_stack[states_on_stack];
00506             state_context=state_context_stack[states_on_stack];
00507 
00508             old_state->leave_state(old_context);
00509         }
00510         else // If there are no states on stack, just egress
00511             egress();
00512     }
00513 
00515 
00516     smach(context_type* machine_context=0):
00517         curr_state(0),
00518         child(0),
00519         state_context(0),
00520         machine_context(machine_context),
00521         default_state(0),
00522         default_context(0),
00523         states_on_stack(0)
00524     { }
00525 
00527     ~smach()
00528     {
00529         egress();
00530 
00531         if(default_state)
00532             default_state->leave_state(default_context);
00533     }
00534 
00536 
00540     void set_child(smach *x)
00541     {
00542 #ifdef ETL_MUTEX_LOCK
00543         ETL_MUTEX_LOCK();
00544 #endif
00545         child=x;
00546     }
00547 
00549     int
00550     state_depth()
00551         { return curr_state?states_on_stack+1:0; }
00552 
00553     event_result
00554     process_event(const event_key& id) { return process_event(event(id)); }
00555 
00557     event_result
00558     process_event(const event& id)
00559     {
00560 #ifdef ETL_MUTEX_LOCK
00561         ETL_MUTEX_LOCK();
00562 #endif
00563 
00564         event_result ret(RESULT_OK);
00565 
00566         // Check for child machine
00567         if(child)
00568         {
00569             ret=child->process_event(id);
00570             if(ret!=RESULT_OK)
00571                 return ret;
00572         }
00573 
00574         try
00575         {
00576             if(curr_state)
00577                 ret=curr_state->process_event(state_context,id);
00578 
00579             if(ret==RESULT_OK)
00580                 return default_state->process_event(default_context,id);
00581 
00582             return ret;
00583         }
00584         catch(egress_exception) { return egress()?RESULT_ACCEPT:RESULT_ERROR; }
00585         catch(pop_exception) { pop_state(); return RESULT_ACCEPT; }
00586         catch(const state_base* state) { return enter(state)?RESULT_ACCEPT:RESULT_ERROR; }
00587     }
00588 
00589 }; // END of template class smach
00590 
00591 _ETL_END_NAMESPACE
00592 
00593 /* === E X T E R N S ======================================================= */
00594 
00595 /* === E N D =============================================================== */
00596 
00597 #endif

Generated on Fri Jun 22 12:59:34 2007 for ETL by  doxygen 1.5.2