- Main Page
- Related Pages
- Classes
- Files

# _smach.h

Go to the documentation of this file.

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