Boost logo

Boost Users :

Subject: Re: [Boost-users] [statechart] Non-Const Machine Pass-throughs and state_cast
From: Grant Erickson (gerickson_at_[hidden])
Date: 2009-08-10 12:06:40


On 8/10/09 12:48 AM, Andreas Huber wrote:
>> The machine is effectively a timer and wraps another internal timer class
>> that can be stopped/started and suspended/resumed. This is all the machine
>> does. The internal timer class can get/set its interval and can also
>> get/set
>> a delegate, that is called (if set) when the timer fires. These internal
>> timer management interfaces are "passed through" to the state machine
>> interface, such that application/test code is as follows:
>
> [snip code]
>
>> Where I have run into a problem is that my machine "pass through" setter
>> methods allowing the delegate to be set or the timer interval to be
>> changed
>> (neither of which materially alter the state of the machine) cannot be
>> called as they are non-const qualified whereas their corresponding getters
>> work fine since they are const qualified.
>
> So both the values of the delegate and the interval are orthogonal to the
> state the machine has? If so, I'm wondering why they're not members of
> struct Timer? This would make ITimerManagement unnecessary and also save
> quite a bit of code?

Andreas:

Making the internal timer a member of the state_machine was precisely my
initial implementation; however, initially, I'd found a destruction ordering
problem with that approach. Basically, on destruction of the running state,
I ensure the timer is stopped:

    struct Running :
        Interfaces::IRunning,
        Interfaces::ISuspended,
        state<Running, Active>
    {
    public:
        typedef mpl::list<
            transition<Events::Stop, Stopped>,
            transition<Events::Suspend, RunningSuspended> > reactions;

        Running(my_context inContext) :
            Interfaces::IRunning(true),
            Interfaces::ISuspended(false),
            my_base(inContext)
        {
            context<Machine::Timer>().Start();
        }
        
        ~Running(void)
        {
            context<Machine::Timer>().Stop();
        }
    };

Initially, the machine and, in turn, the internal timer object appeared to
be destructed before the state, causing an assertion when I tried to stop
the destructed object.

There was also an assertion I had hit on construction of the running state:

    --> Implementation::States::Running::Running()
    Assertion failed: (get_pointer( stt.pContext_ ) != 0), function
context_impl, file boost/statechart/simple_state.hpp, line 683.
   Abort

On revisiting my current implementation, neither of these no longer appear
to be the case. I now wrap the machine in another object and call
.initiate/.terminate:

    SuspendableTimer(void)
    {
        mMachine.initiate();
    }

    ~SuspendableTimer(void)
    {
        mMachine.terminate();
    }

This handles the destruction ordering correctly.

Through iterating on the const state_cast issue, I had also revised the base
of the Running state to 'state' rather than 'simple_state' which eliminated
the construction assertion for that state.

Thanks for the suggestion to revisit that implementation. The conditions had
changed but I hadn't reinvestigated once the conditions changed.

> What is the purpose of the IRunning & ISuspended structs?

They are application-level pseudo-state queries.

Thanks for the tips!

Regards,

Grant


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net