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