00001
00022
00023
00024 #ifndef __ETL__SMACH_H_
00025 #define __ETL__SMACH_H_
00026
00027
00028
00029 #include <vector>
00030 #include <algorithm>
00031 #include <stdexcept>
00032 #include "_mutex_null.h"
00033 #include "_misc.h"
00034
00035
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
00045 #define ETL_MUTEX_LOCK()
00046
00047
00048
00049
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
00077
00078 RESULT_ERROR,
00079 RESULT_OK,
00080 RESULT_ACCEPT,
00081 RESULT_REJECT,
00082
00083 RESULT_END
00084 };
00085
00086
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
00104 friend class smach;
00105
00106
00107 public:
00108 typedef T state_context_type;
00109
00111 typedef event_result (T::*funcptr)(const event&);
00112
00113
00114
00115 event_key id;
00116 funcptr 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
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
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
00216
00217 if(!event_list.size())
00218 low=high=x.id;
00219
00220
00221 event_list.push_back(x);
00222 sort(event_list.begin(),event_list.end());
00223
00224
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
00254 if(nested)
00255 {
00256 const event_result ret(nested->process_event(id));
00257 if(ret!=RESULT_OK)
00258 return ret;
00259 }
00260
00261
00262
00263 if(id.key<low || high<id.key)
00264 return RESULT_OK;
00265
00266
00267 typename std::vector<event_def>::const_iterator iter(find(id.key));
00268
00269
00270 if(iter->id!=id.key)
00271 return RESULT_OK;
00272
00273
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
00286 const state_base* curr_state;
00287 smach* child;
00288
00289 public:
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
00337
00338 const state_base *prev_state=default_state;
00339
00340
00341
00342 if(default_state)
00343 default_state->leave_state(default_context);
00344
00345
00346 default_state=nextstate;
00347 default_context=0;
00348
00349
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
00360 default_state=prev_state;
00361
00362
00363 if(default_state)
00364 default_context=default_state->enter_state(machine_context);
00365
00366
00367
00368 return false;
00369 }
00370
00372
00373 bool
00374 egress()
00375 {
00376 #ifdef ETL_MUTEX_LOCK
00377 ETL_MUTEX_LOCK();
00378 #endif
00379
00380
00381 while(states_on_stack) pop_state();
00382
00383
00384
00385 if(!curr_state)
00386 return true;
00387
00388
00389 bool ret=true;
00390
00391 const state_base* old_state=curr_state;
00392 void *old_context=state_context;
00393
00394
00395 curr_state=0;state_context=0;
00396
00397
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
00415
00416 const state_base *prev_state=curr_state;
00417
00418
00419
00420 if(curr_state)
00421 egress();
00422
00423
00424 curr_state=nextstate;
00425 state_context=0;
00426
00427
00428 state_context=curr_state->enter_state(machine_context);
00429 if(state_context)
00430 return true;
00431
00432
00433 curr_state=prev_state;
00434
00435
00436 if(curr_state)
00437 state_context=curr_state->enter_state(machine_context);
00438
00439
00440
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
00458 if(states_on_stack==SMACH_STATE_STACK_SIZE)
00459 throw(std::overflow_error("smach<>::push_state(): state stack overflow!"));
00460
00461
00462
00463 if(!curr_state && !states_on_stack)
00464 return enter(nextstate);
00465
00466
00467 state_stack[states_on_stack]=curr_state;
00468 state_context_stack[states_on_stack++]=state_context;
00469
00470
00471 curr_state=nextstate;
00472
00473
00474 state_context=curr_state->enter_state(machine_context);
00475 if(state_context)
00476 return true;
00477
00478
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
00494
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
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
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
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 };
00590
00591 _ETL_END_NAMESPACE
00592
00593
00594
00595
00596
00597 #endif