Clarification on statechart post_event vs process_event within react()

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

Clarification on statechart post_event vs process_event within react()

Boost - Users mailing list
Hi,

Apologies if this has already been answered/clarified, but my search of the internet to try to get a definitive answer, at least one I recognised as being definitive, was unsuccessful.

Below I have posted some source code, based on the stopwatch example from the tutorial, but using an 'external' object to delegate the actual handling of event stuff to and using the state, rather than simple_state base class, and some other tweaks.

This is a cut down version of something that someone I work with has devised, but I just wanted to get a clear answer on whether process_event() should be getting called in cases like this. I think it probably should not since, in the code below, you change state (with a state object's destructor being executed) whilst you're executing that state's react(EvStartStop) function.

If someone can confirm this is the correct interpretation that would be very much appreciated.

Any questions, feel free to ask (BTW - I'm compiling for C++17)

John

PS - In case it's of any use, the output from running this code is:

------
Entered Active
Entered Stopped
Stopped - payload2 m_charValue: a
Exited ~Stopped                    <<<--- This is the bit I don't like the look of because...
Entered Running
Active - payload2 m_charValue: a   <<<--- ... this happens from the forward_event() call
payload1 value1: 20
payload1 value2: 200.2
Exited ~Running
Entered Stopped
Exiting Program
Exited ~Stopped
Exited ~Active
------

If I swap the //*** comments to the doProcess() lines instead of doPost(), the output seems more sensible

------
Entered Active
Entered Stopped
Stopped - payload2 m_charValue: a
Exited ~Stopped
Entered Running
Active - payload2 m_charValue: a
payload1 value1: 20
payload1 value2: 200.2
Exited ~Running
Entered Stopped
Exiting Program
Exited ~Stopped
Exited ~Active
------



========== code ===========
#include <boost/statechart/event.hpp>
#include <boost/statechart/state_machine.hpp>
#include <boost/statechart/state.hpp>
#include <boost/statechart/transition.hpp>
#include <boost/statechart/custom_reaction.hpp>
#include <boost/mpl/list.hpp>

#include <iostream>
#include <string>

namespace sc = boost::statechart;
namespace mpl = boost::mpl;

// Forward declare a handler 'class' that's going to have activity delegated to it
struct Handler;

// Normal example start/stop and reset events
struct EvStartStop : sc::event<EvStartStop> {};
struct EvReset : sc::event<EvReset> {};

// A couple of events that carry data with them
struct EvPayload1 : sc::event<EvPayload1>
{
    EvPayload1(int value1, double value2) : m_value1 { value1 }, m_value2 { value2 } {};
    int m_value1 { 0 };
    double m_value2 { 0.0 };
};
struct EvPayload2 : sc::event<EvPayload2>
{
    EvPayload2(const char& charValue) : m_charValue { charValue } {};
    char m_charValue { false };
};

// Back to the example bits
struct Active;
struct StopWatch : sc::state_machine<StopWatch, Active>
{
    typedef sc::transition<EvReset, Active> reactions;

    // The extra 'handler' stuff
    void setHandler(Handler* handler) { m_handler = handler; };
    Handler* getHandler() const { return m_handler; };
    Handler* m_handler { nullptr };

    // Used to make post_event() accessible from an external class...seems unpleasant, but...
    using sc::state_machine<StopWatch, Active>::post_event;
};

// The implementation of the Handler class; created with a reference to a StopWatch object
struct Handler
{
    Handler(StopWatch& stopWatch) : m_stopWatch { stopWatch } {};

    void doPost()
    {
        m_stopWatch.post_event(EvStartStop());
    }

    void doProcess()
    {
        m_stopWatch.process_event(EvStartStop());
    }

    StopWatch& m_stopWatch;
};

struct Stopped;
struct Active : sc::state<Active, StopWatch, Stopped>
{
    Active(my_context ctx) : my_base { ctx }  { std::cout << "Entered Active" << std::endl; };
    ~Active() { std::cout << "Exited ~Active" << std::endl; };

    typedef mpl::list<sc::custom_reaction<EvPayload1>,
                      sc::custom_reaction<EvPayload2>,
                      sc::transition<EvReset, Active>> reactions;

    sc::result react(const EvPayload1& payload1)
    {
        std::cout << "payload1 Event NOT HANDLED" << std::endl;
        return discard_event();
    }

    sc::result react(const EvPayload2& payload2)
    {
        std::cout << "Active - payload2 m_charValue: " << payload2.m_charValue << std::endl;
        return discard_event();
    }
};

struct Running : sc::state<Running, Active>
{
    Running(my_context ctx) : my_base { ctx } { std::cout << "Entered Running" << std::endl; };
    ~Running() { std::cout << "Exited ~Running" << std::endl; };

    typedef mpl::list<sc::custom_reaction<EvPayload1>,
                      sc::transition<EvStartStop, Stopped>> reactions;

    sc::result react(const EvPayload1& payload1)
    {
        std::cout << "payload1 value1: " << payload1.m_value1 << std::endl;
        std::cout << "payload1 value2: " << payload1.m_value2 << std::endl;

        //***context<StopWatch>().getHandler()->doPost();
        context<StopWatch>().getHandler()->doProcess();

        return discard_event();
    }
};

struct Stopped : sc::state<Stopped, Active>
{
    Stopped(my_context ctx) : my_base { ctx } { std::cout << "Entered Stopped" << std::endl; };
    ~Stopped() { std::cout << "Exited ~Stopped" << std::endl; };

    typedef mpl::list<sc::custom_reaction<EvPayload2>,
                      sc::transition<EvStartStop, Running>> reactions;

    sc::result react(const EvPayload2& payload2)
    {
        std::cout << "Stopped - payload2 m_charValue: " << payload2.m_charValue << std::endl;

        //***context<StopWatch>().getHandler()->doPost();
        context<StopWatch>().getHandler()->doProcess();

        return forward_event();
    }
};

int main()
{
    StopWatch myWatch;
    Handler myHandler(myWatch);
    myWatch.setHandler(&myHandler);

    myWatch.initiate();
    myWatch.process_event(EvPayload2 { 'a' });
    myWatch.process_event(EvPayload1 { 20, 200.2 });
    std::cout << "Exiting Program" << std::endl;
    return 0;
}
_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users