ETL  0.04.19
_smach.h
Go to the documentation of this file.
1 
23 /* === S T A R T =========================================================== */
24 
25 #ifndef __ETL__SMACH_H_
26 #define __ETL__SMACH_H_
27 
28 /* === H E A D E R S ======================================================= */
29 
30 #include <vector>
31 #include <algorithm>
32 #include <stdexcept>
33 #include "_mutex_null.h"
34 #include "_misc.h"
35 
36 /* === M A C R O S ========================================================= */
37 
38 #define SMACH_STATE_STACK_SIZE (32)
39 
40 #ifdef _MSC_VER
41 #pragma warning (disable:4786)
42 #pragma warning (disable:4290) // MSVC6 doesn't like function declarations with exception specs
43 #endif
44 
45 //#define ETL_MUTEX_LOCK() _mutex::lock lock(mutex)
46 #define ETL_MUTEX_LOCK()
47 
48 /* === T Y P E D E F S ===================================================== */
49 
50 /* === C L A S S E S & S T R U C T S ======================================= */
51 
53 
60 template <typename CON, typename K=int, typename M=mutex_null>
61 class smach
62 {
63 public:
64 
65  typedef K event_key;
66  typedef M _mutex;
67  typedef CON context_type;
68 
69 
70  struct egress_exception { };
71  struct pop_exception { };
72 
73 
76  {
77  // These values are returned by the event
78  // handlers cast to state pointers.
83 
85  };
86 
87  //template<typename T> class state;
88 
90  struct event
91  {
93 
94  event() { }
95  event(const event_key& key):key(key) { }
96 
97  operator event_key()const { return key; }
98  };
99 
101  template<typename T>
103  {
104  // List our friends
105  friend class smach;
106  //friend class state<T>;
107 
108  public:
110 
112  typedef event_result (T::*funcptr)(const event&);
113 
114  //private:
115 
116  event_key id; //<! Event ID
117  funcptr handler; //<! Pointer event handler
118 
119  public:
120 
122  bool operator<(const event_def_internal &rhs)const
123  { return id<rhs.id; }
124 
126  bool operator==(const event_def_internal &rhs)const
127  { return id==rhs.id; }
128 
130  bool operator<(const event_key &rhs)const
131  { return id<rhs; }
132 
134  bool operator==(const event_key &rhs)const
135  { return id==rhs; }
136 
139 
142 
145 
146  };
147 
149  {
150  // Our parent is our friend
151  friend class smach;
152  public:
153  virtual ~state_base() { }
154 
155  virtual void* enter_state(context_type* machine_context)const=0;
156 
157  virtual bool leave_state(void* state_context)const=0;
158 
159  virtual event_result process_event(void* state_context,const event& id)const=0;
160 
161  virtual const char *get_name() const=0;
162  };
163 
165  template<typename T>
166  class state : public state_base
167  {
168  // Our parent is our friend
169  friend class smach;
170 
171  public:
174 
175 
176  private:
177 
178  std::vector<event_def> event_list;
179 
182  const char *name;
184 
185  public:
186 
188  state(const char *n, smach* nest=0):
189  nested(nest),name(n),default_handler(NULL)
190  { }
191 
192  virtual ~state() { }
193 
195 
196  void set_nested_machine(smach *sm) { nested=sm; }
197 
200 
202  virtual const char *get_name() const { return name; }
203 
205  void
206  insert(const event_def &x)
207  {
208  // If this is our first event_def,
209  // setup the high and low values.
210  if(!event_list.size())
211  low=high=x.id;
212 
213  // Sort the event_def onto the list
214  event_list.push_back(x);
215  sort(event_list.begin(),event_list.end());
216 
217  // Update the low and high markers
218  if(x.id<low)
219  low=x.id;
220  if(high<x.id)
221  high=x.id;
222  }
223 
224  typename std::vector<event_def>::iterator find(const event_key &x) { return binary_find(event_list.begin(),event_list.end(),x); }
225  typename std::vector<event_def>::const_iterator find(const event_key &x)const { return binary_find(event_list.begin(),event_list.end(),x); }
226 
227  protected:
228 
230  {
231  return new state_context_type(machine_context);
232  }
233 
234  virtual bool leave_state(void* x)const
235  {
236  state_context_type* state_context(reinterpret_cast<state_context_type*>(x));
237  delete state_context;
238  return true;
239  }
240 
241  virtual event_result
242  process_event(void* x,const event& id)const
243  {
244  state_context_type* state_context(reinterpret_cast<state_context_type*>(x));
245 
246  // Check for nested machine in state
247  if(nested)
248  {
249  const event_result ret(nested->process_event(id));
250  if(ret!=RESULT_OK)
251  return ret;
252  }
253 
254  // Quick test to make sure that the
255  // given event is in the state
256  if(id.key<low || high<id.key)
257  return RESULT_OK;
258 
259  // Look for the event
260  typename std::vector<event_def>::const_iterator iter(find(id.key));
261 
262  // If search results were negative, fail.
263  if(iter->id!=id.key)
264  return RESULT_OK;
265 
266  // Execute event function
267  event_result ret((state_context->*(iter->handler))(id));
268 
269  if(ret==RESULT_OK && default_handler)
270  ret=(state_context->*(default_handler))(id);
271 
272  return ret;
273  }
274  };
275 
276 private:
277 
278  // Machine data
281 
282 public: // this really should be private
284 private:
285 
287 
290 
291 #ifdef ETL_MUTEX_LOCK
293 #endif
294 
299 
300 public:
301 
303  const char *
305  {
306 #ifdef ETL_MUTEX_LOCK
307  ETL_MUTEX_LOCK();
308 #endif
309  if(curr_state)
310  return curr_state->get_name();
311  if(default_state)
312  return default_state->get_name();
313  return 0;
314  }
315 
317 
319  static bool
321  { return rhs<=RESULT_ERROR; }
322 
323  bool
324  set_default_state(const state_base *nextstate)
325  {
326 #ifdef ETL_MUTEX_LOCK
327  ETL_MUTEX_LOCK();
328 #endif
329  // Keep track of the current state unless
330  // the state switch fails
331  const state_base *prev_state=default_state;
332 
333  // If we are already in a state, leave it and
334  // collapse the state stack
335  if(default_state)
337 
338  // Set this as our current state
339  default_state=nextstate;
340  default_context=0;
341 
342  // Attempt to enter the state
343  if(default_state)
344  {
346  if(default_context)
347  return true;
348  }
349  else
350  return true;
351 
352  // We failed, so attempt to return to previous state
353  default_state=prev_state;
354 
355  // If we had a previous state, enter it
356  if(default_state)
358 
359  // At this point we are not in the
360  // requested state, so return failure
361  return false;
362  }
363 
365 
366  bool
368  {
369 #ifdef ETL_MUTEX_LOCK
370  ETL_MUTEX_LOCK();
371 #endif
372 
373  // Pop all states off the state stack
374  while(states_on_stack) pop_state();
375 
376  // If we are not in a state, then I guess
377  // we were successful.
378  if(!curr_state)
379  return true;
380 
381  // Grab the return value from the exit function
382  bool ret=true;
383 
384  const state_base* old_state=curr_state;
385  void *old_context=state_context;
386 
387  // Clear out the current state and its state_context
389 
390  // Leave the state
391  return old_state->leave_state(old_context);
392 
393  return ret;
394  }
395 
397 
400  bool
401  enter(const state_base *nextstate)
402  {
403 #ifdef ETL_MUTEX_LOCK
404  ETL_MUTEX_LOCK();
405 #endif
406 
407  // Keep track of the current state unless
408  // the state switch fails
409  const state_base *prev_state=curr_state;
410 
411  // If we are already in a state, leave it and
412  // collapse the state stack
413  if(curr_state)
414  egress();
415 
416  // Set this as our current state
417  curr_state=nextstate;
418  state_context=0;
419 
420  // Attempt to enter the state
422  if(state_context)
423  return true;
424 
425  // We failed, so attempt to return to previous state
426  curr_state=prev_state;
427 
428  // If we had a previous state, enter it
429  if(curr_state)
431 
432  // At this point we are not in the
433  // requested state, so return failure
434  return false;
435  }
436 
438 
443  bool
444  push_state(const state_base *nextstate)
445  {
446 #ifdef ETL_MUTEX_LOCK
447  ETL_MUTEX_LOCK();
448 #endif
449 
450  // If there are not enough slots, then throw something.
452  throw(std::overflow_error("smach<>::push_state(): state stack overflow!"));
453 
454  // If there is no current state, nor anything on stack,
455  // just go ahead and enter the given state.
456  if(!curr_state && !states_on_stack)
457  return enter(nextstate);
458 
459  // Push the current state onto the stack
462 
463  // Make the next state the current state
464  curr_state=nextstate;
465 
466  // Try to enter the next state
468  if(state_context)
469  return true;
470 
471  // Unable to push state, return to old one
474  return false;
475  }
476 
478 
479  void
481  {
482 #ifdef ETL_MUTEX_LOCK
483  ETL_MUTEX_LOCK();
484 #endif
485 
486  // If we aren't in a state, then there is nothing
487  // to do.
488  if(!curr_state)
489  throw(std::underflow_error("smach<>::pop_state(): stack is empty!"));
490 
491  if(states_on_stack)
492  {
493  const state_base* old_state=curr_state;
494  void *old_context=state_context;
495 
496  // Pop previous state off of stack
497  --states_on_stack;
500 
501  old_state->leave_state(old_context);
502  }
503  else // If there are no states on stack, just egress
504  egress();
505  }
506 
508 
510  curr_state(0),
511  child(0),
512  state_context(0),
514  default_state(0),
515  default_context(0),
516  states_on_stack(0)
517  { }
518 
521  {
522  egress();
523 
524  if(default_state)
526  }
527 
529 
533  void set_child(smach *x)
534  {
535 #ifdef ETL_MUTEX_LOCK
536  ETL_MUTEX_LOCK();
537 #endif
538  child=x;
539  }
540 
542  int
544  { return curr_state?states_on_stack+1:0; }
545 
547  process_event(const event_key& id) { return process_event(event(id)); }
548 
551  process_event(const event& id)
552  {
553 #ifdef ETL_MUTEX_LOCK
554  ETL_MUTEX_LOCK();
555 #endif
556 
557  event_result ret(RESULT_OK);
558 
559  // Check for child machine
560  if(child)
561  {
562  ret=child->process_event(id);
563  if(ret!=RESULT_OK)
564  return ret;
565  }
566 
567  try
568  {
569  if(curr_state)
571 
572  if(ret==RESULT_OK)
574 
575  return ret;
576  }
577  catch(egress_exception) {
578  if (egress()) {
579  ret=RESULT_ACCEPT;
580  } else {
581  ret=RESULT_ERROR;
582  }
583  }
584  catch(pop_exception) { pop_state(); return RESULT_ACCEPT; }
585  catch(const state_base* state) { return enter(state)?RESULT_ACCEPT:RESULT_ERROR; }
586  return ret;
587  }
588 
589 }; // END of template class smach
590 
592 
593 /* === E X T E R N S ======================================================= */
594 
595 /* === E N D =============================================================== */
596 
597 #endif