Boost logo

Boost :

Subject: [boost] [statechart] Bug when switching inner-states?
From: George van Venrooij (george.van.venrooij_at_[hidden])
Date: 2009-04-01 08:51:06


Hi,

   I have attached a test program that gives some curious results,
indicating the statemachine does not reach its proper state when
performing state changes in orthogonal regions.

   The machine attached contains a main state that handles an "update"
event.

   That main state contains 2 inner states, one of which also handles
the "update" event.

   If that inner state transits to another state and back again, the
"update" event no longer arrives at it, which in my opinion is a bug.

   If I modify the state machine so it does not have 2 but only 1 inner
state, then the behavior matches my expectations.

   Could someone please verify if this is bug or tell me if my usage
scenerio is incorrect.

Regards,

   George van Venrooij
   Organic Vectory
   http://www.organicvectory.com


#include <boost/statechart/state_machine.hpp>
#include <boost/statechart/event.hpp>
#include <boost/statechart/simple_state.hpp>
#include <boost/statechart/custom_reaction.hpp>

#include <iostream>

namespace bs = boost::statechart;

using namespace std;

// The main event, should be handles by a sub-state and the main state
struct update_event;
// An event triggering the sub-state to switch
struct toggle_event;
// A dummy event
struct other_event;

// Sub-states in 2 orthogonal regions
struct state_1A;
struct state_1B;
struct state_2A;
struct state_2B;

// Main state
struct parent_state;

// State machine consisting of the main state
struct machine : bs::state_machine<machine, parent_state>
{
};

typedef
boost::mpl::list
< state_1A
, state_2A
> inner_states_t;

// The main state has 2 orthogonal sub-states
struct parent_state : bs::simple_state<parent_state, machine, inner_states_t>
{
    typedef boost::mpl::list
        < bs::custom_reaction <update_event>
>
        reactions;

    bs::result react(const update_event&)
    {
        cout << __FUNCTION__ << endl;

        return discard_event();
    }
};

struct update_event : bs::event<update_event>
{
};

struct other_event : bs::event<other_event>
{
};

struct toggle_event : bs::event<toggle_event>
{
};

struct state_1A : bs::simple_state<state_1A, parent_state::orthogonal<0>>
{
    typedef boost::mpl::list
        < bs::custom_reaction <toggle_event>
        , bs::custom_reaction <update_event>
>
        reactions;

    state_1A()
    {
        cout << __FUNCTION__ << endl;
    }

    bs::result react(const toggle_event&)
    {
        cout << __FUNCTION__ << endl;

        return transit<state_1B>();
    }

    bs::result react(const update_event&)
    {
        cout << __FUNCTION__ << endl;

        return forward_event();
    }
};

struct state_1B : bs::simple_state<state_1B, parent_state::orthogonal<0>>
{
    typedef boost::mpl::list
        < bs::custom_reaction <toggle_event>
>
        reactions;

    state_1B()
    {
        cout << __FUNCTION__ << endl;
    }

    bs::result react(const toggle_event&)
    {
        cout << __FUNCTION__ << endl;

        return transit<state_1A>();
    }
};

struct state_2A : bs::simple_state<state_2A, parent_state::orthogonal<1>>
{
    typedef boost::mpl::list
        < bs::custom_reaction <other_event>
>
        reactions;

    state_2A()
    {
        cout << __FUNCTION__ << endl;
    }

    bs::result react(const other_event&)
    {
        cout << __FUNCTION__ << endl;

        return discard_event();
    }
};

struct state_2B : bs::simple_state<state_2B, parent_state::orthogonal<1>>
{
    typedef boost::mpl::list
        < bs::custom_reaction <other_event>
>
        reactions;

    state_2B()
    {
        cout << __FUNCTION__ << endl;
    }

    bs::result react(const other_event&)
    {
        cout << __FUNCTION__ << endl;

        return discard_event();
    }
};

int main(int argc, char* argv[])
{
    machine m;

    cout << "0. Initiating state machine:" << endl;
    m.initiate();

    cout << endl << "1. Processing 'other_event':" << endl;
    m.process_event(other_event ());

    cout << endl << "2. Processing 'update_event':" << endl;
    m.process_event(update_event());

    cout << endl << "3. Processing 'toggle_event':" << endl;
    m.process_event(toggle_event());

    cout << endl << "4. Processing 'update_event':" << endl;
    m.process_event(update_event());

    cout << endl << "5. Processing 'toggle_event':" << endl;
    m.process_event(toggle_event());

    cout << endl << "6. Processing 'update_event':" << endl;
    m.process_event(update_event());

    return 0;
}

//////////////////////////////////////////////////////////////////////////
// Program output after building with VC 9 and Boost 1.37.0
//
// Note the different event handling in step 6. as compared to step 2.
// although the machine should be back in the same state.
//////////////////////////////////////////////////////////////////////////

/*

0. Initiating state machine:
state_1A::state_1A
state_2A::state_2A

1. Processing 'other_event':
state_2A::react

2. Processing 'update_event':
state_1A::react
parent_state::react

3. Processing 'toggle_event':
state_1A::react
state_1B::state_1B

4. Processing 'update_event':
parent_state::react

5. Processing 'toggle_event':
state_1B::react
state_1A::state_1A

6. Processing 'update_event':
parent_state::react

*/


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk