随笔-383  评论-37  文章-0  trackbacks-0

1.2 增加动作


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

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

34


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


1.3 State-local存储


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

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

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

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

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

33   private :
34    std:: time_t  startTime_;
35}
;

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

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

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

posted on 2010-10-03 19:09 小王 阅读(814) 评论(0)  编辑 收藏 引用 所属分类: Boost