随笔-60  评论-111  文章-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所处不同的状态,我们需要不同的变量。

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

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

posted on 2010-08-16 14:53 shaker(太子) 阅读(1927) 评论(1)  编辑 收藏 引用 所属分类: C++

评论:
# re: Boost的状态机库教程(3) 2011-05-08 14:01 | zbjxb
"然而,我们不是先问一下状态机在什么状态,然后再去用不同的方法计算逝去时间,而是将计算放到Stopped和Running状态中,用一个接口来获得逝去逝去时间。"
----太漂亮了。  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理