Boost的状态机库教程(3)

来源:互联网 发布:环信用java接入 编辑:程序博客网 时间:2024/05/16 16:18



1.2 增加动作


    此时我们将只用一种动作:transitions,我们在下面的代码中插入了黑体的部分。

 

 

  1. #include <boost/statechart/transition.hpp> 
  2.  
  3. // ... 
  4.  
  5. struct Stopped; 
  6. struct Active : sc::simple_state< Active, StopWatch, Stopped > 
  7.   typedef sc::transition< EvReset, Active > reactions; 
  8. }; 
  9.  
  10. struct Running : sc::simple_state< Running, Active > 
  11.   typedef sc::transition< EvStartStop, Stopped > reactions; 
  12. }; 
  13.  
  14. struct Stopped : sc::simple_state< Stopped, Active > 
  15.   typedef sc::transition< EvStartStop, Running > reactions; 
  16. }; 
  17.  
  18. //一个状态可以定义任意数量的动作。这就是为什么当多于一个时,
  19. //我们不得不将它们放到一个mpl::list<> 里。 
  20.  
  21. int main() 
  22.   StopWatch myWatch; 
  23.   myWatch.initiate(); 
  24.   myWatch.process_event( EvStartStop() ); 
  25.   myWatch.process_event( EvStartStop() ); 
  26.   myWatch.process_event( EvStartStop() ); 
  27.   myWatch.process_event( EvReset() ); 
  28.   return 0; 


    现在我们有了所有的状态,并在适当的位置增加了所有的迁移动作,同时我们也向StopWatch发送了一些事件。这个状态机会尽职尽责的按我们的希望进行状态迁移,但依然现在还没有其它的动作。


1.3 State-local存储


    下一步我们将让这个Stop watch真正的记录时间了。根据stop watch所处不同的状态,我们需要不同的变量。

Stopped状态:需要一个保存逝去时间的变量。

l Running状态:需要一个保存逝去时间的变量,还需要一个保存上一次启动的时间点的变量。

    无论状态机在什么状态下,我们都必须观察逝去时间这个变量。此外,当我们向状态机发送EvReSet事件时,这个变量应该被置为0。其它的变量只是状态机在Running状态时需要。无论何时我们进入Running状态时,它应该被置为系统时钟的当前时间。当我们退出Running状态时,我们仅仅从系统时钟的当前时间减去开始时间(进入时记录的时间),将结果加到逝去时间里就可以了。

 

  1. #include <ctime> 
  2.  
  3. // ... 
  4.  
  5. struct Stopped; 
  6. struct Active : sc::simple_state< Active, StopWatch, Stopped > 
  7.   public: 
  8.     typedef sc::transition< EvReset, Active > reactions; 
  9.  
  10.     Active() : elapsedTime_( 0.0 ) {} 
  11.     double ElapsedTime() const { return elapsedTime_; } 
  12.     double & ElapsedTime() { return elapsedTime_; } 
  13.   private
  14.     double elapsedTime_
  15. }; 
  16.  
  17. struct Running : sc::simple_state< Running, Active > 
  18.   public: 
  19.     typedef sc::transition< EvStartStop, Stopped > reactions; 
  20.  
  21.     Running() : startTime_( std::time( 0 ) ) {} 
  22.     ~Running() 
  23.     { 
  24.       // 与派生类可以访问它的基类相似,
  25.       //context<>() 用来获得一个状态的直接或间接的上下文的访问权。
  26.       // 这可以是直接或间接的外层状态或状态机本身
  27.       // (例如,像这样: context< StopWatch >()). 
  28.       context< Active >().ElapsedTime() += 
  29.         std::difftime( std::time( 0 ), startTime_ ); 
  30.     } 
  31.   private
  32.     std::time_t startTime_; 
  33. };

    这个状态机现在可以测量时间了,但是我们还不能看到结果。

在这里,State-local storage的优势还没有完成显现出来。在FAQ项目“State-local storage酷在哪里?”中,会通过与一个没有用State-local storage的Stop Watch的比较来说明。


1.4 在状态机外得到状态信息


    为了取得测量的时间,我们需要一个从状态机外得到状态信息的机制。按我们现在的状态机设计,可以有两种方法。为简单起见,我们在这里用一个低效的方式:state_cast<>()(在StopWatch2.cpp中我们会用一个稍复杂一点的替代方法)(译者注:在StopWatch2.cpp中是向状态机发送一个取得逝去时间的事件,从事件成员量中将逝去时间带回来),从字面意思就可以看出,它在语义上与dynamic_cast有点相似。例如,当我们调用myWatch.state_cast<const Stpped&>()时,当状态机在Stopped状态时,我们会得到一个Stopped状态类的引用。否则,会抛出std::bad_cast异常。我们可以利用这个功能来实现一个StopWatch的成员函数,让它的结果返回逝去的时间。然而,我们不是先问一下状态机在什么状态,然后再去用不同的方法计算逝去时间,而是将计算放到Stopped和Running状态中,用一个接口来获得逝去逝去时间。

 

  1. #include <iostream> 
  2.  
  3. // ... 
  4.  
  5. struct IElapsedTime 
  6.   virtual double ElapsedTime() const = 0; 
  7. }; 
  8.  
  9. struct Active; 
  10. struct StopWatch : sc::state_machine< StopWatch, Active > 
  11.   double ElapsedTime() const 
  12.   { 
  13.     return state_cast< const IElapsedTime & >().ElapsedTime(); 
  14.   } 
  15. }; 
  16.  
  17. // ... 
  18.  
  19. struct Running : IElapsedTime, 
  20.   sc::simple_state< Running, Active > 
  21.   public
  22.     typedef sc::transition< EvStartStop, Stopped > reactions; 
  23.  
  24.     Running() : startTime_( std::time( 0 ) ) {} 
  25.     ~Running() 
  26.     { 
  27.       context< Active >().ElapsedTime() = ElapsedTime(); 
  28.     }
  29.     virtual double ElapsedTime() const 
  30.         { 
  31.           return context< Active >().ElapsedTime() + 
  32.             std::difftime( std::time( 0 ), startTime_ ); 
  33.         } 
  34.       private
  35.         std::time_t startTime_; 
  36.     }; 
  37.      
  38.     struct Stopped : IElapsedTime, 
  39.       sc::simple_state< Stopped, Active > 
  40.     { 
  41.       typedef sc::transition< EvStartStop, Running > reactions; 
  42.      
  43.       virtual double ElapsedTime() const 
  44.       { 
  45.         return context< Active >().ElapsedTime(); 
  46.       } 
  47.     }; 
  48.      
  49.     int main() 
  50.     { 
  51.       StopWatch myWatch; 
  52.       myWatch.initiate(); 
  53.       std::cout << myWatch.ElapsedTime() << "/n"; 
  54.       myWatch.process_event( EvStartStop() ); 
  55.       std::cout << myWatch.ElapsedTime() << "/n"
  56.       myWatch.process_event( EvStartStop() ); 
  57.       std::cout << myWatch.ElapsedTime() << "/n"
  58.       myWatch.process_event( EvStartStop() ); 
  59.       std::cout << myWatch.ElapsedTime() << "/n"
  60.       myWatch.process_event( EvReset() ); 
  61.       std::cout << myWatch.ElapsedTime() << "/n"
  62.       return 0; 
  63.     }

为了确实看到被测量的时间,你应该想办法在main()中单步执行。StopWatch例子将这个程序扩展为一个交互式的终端程序了。


 

原创粉丝点击