(转)Boost的状态机库教程(3)

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状态:需要一个保存逝去时间的变量。

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状态中,用一个接口来获得逝去逝去时间。

#include <iostream> 
// ...
struct IElapsedTime
{
virtual double ElapsedTime() const = 0;
};
struct Active;
struct StopWatch : sc::state_machine< StopWatch, Active >
{
double ElapsedTime() const
{
return state_cast< const IElapsedTime & >().ElapsedTime();
}
};
// ...
struct Running : IElapsedTime,
sc::simple_state< Running, Active >
{
public :
typedef sc::transition< EvStartStop, Stopped > reactions;
Running() : startTime_( std::time( 0 ) ) {}
~Running()
{
context< Active >().ElapsedTime() = ElapsedTime();
}
virtual double ElapsedTime() const
{
return context< Active >().ElapsedTime() +
std::difftime( std::time( 0 ), startTime_ );
}
private :
std:: time_t startTime_;
};

struct Stopped : IElapsedTime,
sc::simple_state< Stopped, Active >
{
typedef sc::transition< EvStartStop, Running > reactions;

virtual double ElapsedTime() const
{
return context< Active >().ElapsedTime();
}
};

int main()
{
StopWatch myWatch;
myWatch.initiate();
std::cout << myWatch.ElapsedTime() << "\n" ;
myWatch.process_event( EvStartStop() );
std::cout << myWatch.ElapsedTime() << "\n" ;
myWatch.process_event( EvStartStop() );
std::cout << myWatch.ElapsedTime() << "\n" ;
myWatch.process_event( EvStartStop() );
std::cout << myWatch.ElapsedTime() << "\n" ;
myWatch.process_event( EvReset() );
std::cout << myWatch.ElapsedTime() << "\n" ;
return 0;
}

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



posted @ 2011-12-13 13:58  且听风吟~  阅读(502)  评论(0编辑  收藏  举报