1

I have implemented a state machine that inherits boost::statechart. When I call fsm.process_event( some_event() ) which reaction is expected to throw exception it turns out that after I handle the exception with try-catch block my statemachine instance fsm is terminated. That is, fsm.terminated() returns true. In some cases I don't want it to get terminated. Like when I want the statemachine to throw exception to inform the caller of fsm.process_event( irrelevant_event() ) for non-handled event and to keep its current prior to the event state.

In short - how can I prevent boost::statechart from terminating after it throws an exception and keep its prior to the exception state?

Example Code:

namespace sc = boost::statechart;
class State;
struct some_event : public sc::event<some_event> { };

class FSM
    : public sc::state_machine< FSM, State, std::allocator<void>, sc::exception_translator<> >
{
public:
    FSM()
    {
        cout<<"FSM::FSM()"<<endl;
    }
    virtual ~FSM()
    {
        cout<<"FSM::~FSM()"<<endl;
    }
};


class State : public sc::simple_state< State, FSM >
{
public:
    State()
    {
        cout<<"State::State()"<<endl;
    }
    virtual ~State()
    {
        cout<<"State::~State()"<<endl;
    }

    typedef boost::mpl::list<
        sc::custom_reaction< some_event >,
        sc::custom_reaction< sc::exception_thrown >
    > reactions;
    sc::result react( const some_event & e)
    {
        cout<<"State::react( const some_event &)"<<endl;
        throw std::exception();
        return this->discard_event();
    }
    sc::result react( const sc::exception_thrown & e)
    {
        cout<<"State::react( const sc::exception_thrown &)"<<endl;
        throw;
        return this->discard_event();
    }
};

int main()
{
    FSM fsm;
    fsm.initiate();

    try
    {
        fsm.process_event(some_event());
    }
    catch(...)
    {
        cout<<"Exception caught"<<endl;
    }


    if(fsm.terminated())
    {
        cout<<"fsm2 is TERMINATED"<<endl;
    }
    else
    {
        cout<<"fsm2 is RUNNING"<<endl;
    }
    return 0;
}

Code Output:

FSM::FSM()
State::State()
State::react( const some_event &)
State::react( const sc::exception_thrown &)
State::~State()
Exception caught
fsm2 is TERMINATED

I want it to Output:

FSM::FSM()
State::State()
State::react( const some_event &)
State::react( const sc::exception_thrown &)
State::~State()
Exception caught
fsm2 is RUNNING
4

1 回答 1

1

您应该为您的状态机提供自定义异常处理程序。请参阅此处的提升文档:http: //www.boost.org/doc/libs/1_55_0/libs/statechart/doc/tutorial.html#ExceptionHandling

当抛出异常时,状态机不可能知道它是否仍然处于有效状态,这就是为什么异常句柄的默认操作是终止 sm。您的自定义处理程序可以进行清理/检查以确保 sm 处于有效状态并在不同的庄园中向上传播信息。

就我个人而言,我从来没有看到过通过异常从 SM 中传播信息的充分理由。这很可能是因为我从未在您的特定领域工作过,但这里是我的理性:

如果该事件无关紧要,则忽略它或记录它,同一事件可能与另一种状态相关,而与当前状态无关。如果事件无效,即永远不会发生或状态不正确,则为:

  • 您的代码有问题,您应该立即断言并处理该问题
  • 来自包含 SM 的模块之外的无效输入的问题(硬件连续发布 3 个断开连接的事件,这永远不可能等等)。在这种情况下,您无法以任何方式在本地模块中正确处理异常,最好的办法是记录问题并切换到 CatastrophicErrorState 或只能留下 EvReset 或其他东西的东西。
于 2014-02-28T08:16:07.400 回答