Boost logo

Boost Users :

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


I am using Boost::Statechart for the first time, throwing in the bin a
history of hand-coded and other class-based implementations of my own
design, to realize a rather simple machine consisting of four events
(start/stop/suspend/resume) and four states
(stopped/running/stopped-suspended/running-suspended).

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:

    ...(TimerDelegate & inDelegate, float inInterval)
    {
        Timer theTimer;
     
        theTimer.initiate();

        BOOST_ASSERT(!theTimer.IsSuspended());
        BOOST_ASSERT(!theTimer.IsRunning());
        DisplayState(theTimer);

        theTimer.SetDelegate(inDelegate);
        theTimer.SetInterval(inInterval);

        cout << "The delegate is " << theTimer.GetDelegate() << endl;
        cout << "The interval is " << theTimer.GetInterval() << endl;

        BOOST_ASSERT(!theTimer.IsSuspended());
        BOOST_ASSERT(!theTimer.IsRunning());
        DisplayState(theTimer);

        theTimer.process_event(Events::Start());

        BOOST_ASSERT(!theTimer.IsSuspended());
        BOOST_ASSERT(theTimer.IsRunning());
        DisplayState(theTimer);

The delegate does the real work of the program and receives events that
drive the timer machine.

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.

I've worked around this in the short term by qualifying the setters as const
and then qualifying the data members as mutable; however, this strikes me as
flat-out ugly and wrong. Is there a better way to accomplish this? Am I
misusing the template/design pattern? I suppose I could applying a
const_cast(); however, that seems just as bad as my existing workaround.

    class InternalTimer;
    
    struct TimerDelegate
    {
        virtual void TimerDidFire(const InternalTimer &inTimer) const = 0;
    };
    
    class InternalTimer
    {
    public:
        InternalTimer(void); :
        ~InternalTimer(void);
        TimerDelegate *GetDelegate(void) const;
        float GetInterval(void) const;
        void SetDelegate(TimerDelegate *inDelegate);
        void SetInterval(float inInterval);
        void Start(void);
        void Stop(void);
    
    private:
        TimerDelegate * mDelegate;
        float mInterval;
    };
    
    struct ITimerManagement
    {
    public:
        virtual TimerDelegate *GetDelegate(void) const = 0;
        virtual float GetInterval(void) const = 0;
        virtual void SetDelegate(TimerDelegate * inDelegate) = 0;
        virtual void SetInterval(float inInterval) = 0;
    };
    
    struct IRunning
    {
    public:
        virtual bool IsRunning(void) const;
    
    protected:
        IRunning(bool inRunning);
    
    private:
        bool mRunning;
    };
    
    struct ISuspended
    {
    public:
        virtual bool IsSuspended(void) const;
    
    protected:
        ISuspended(bool inSuspended);
    
    private:
        bool mSuspended;
    };
    
    struct Active;
    struct Timer :
        state_machine< Timer, Active >
    {
    public:
        TimerDelegate * GetDelegate(void) const
        {
            return (state_cast< const ITimerManagement & >().GetDelegate());
        }
    
        float GetInterval(void) const
        {
            return (state_cast< const ITimerManagement & >().GetInterval());
        }
    
        void SetDelegate(TimerDelegate *inDelegate)
        {
            state_cast< ITimerManagement & >().SetDelegate(inDelegate); //
XXX
        }
    
        void SetInterval(float inInterval)
        {
            state_cast< ITimerManagement & >().SetInterval(inInterval); //
XXX
        }
    
        bool IsRunning(void) const
        {
            return (state_cast< const IRunning & >().IsRunning());
        }
    
        bool IsSuspended(void) const
        {
            return (state_cast< const ISuspended & >().IsSuspended());
        }
    };
    
    struct Timer;
    struct Stopped;
    struct Running;
    struct StoppedSuspended;
    struct RunningSuspended;
    
    struct Active :
        ITimerManagement,
        simple_state< Active, Timer, Stopped >
    {
        virtual TimerDelegate * GetDelegate(void) const
        {
            return (mInternalTimer.GetDelegate());
        }
    
        virtual float GetInterval(void) const
        {
            return (mInternalTimer.GetInterval());
        }
    
        virtual void SetDelegate(TimerDelegate *inDelegate)
        {
            mInternalTimer.SetDelegate(inDelegate);
        }
    
        virtual void SetInterval(float inInterval)
        {
            mInternalTimer.SetInterval(inInterval);
        }
    
        void Start(void)
        {
            mInternalTimer.Start();
        }
    
        void Stop(void)
        {
            mInternalTimer.Stop();
        }
    
    private:
        InternalTimer mInternalTimer;
    };
    
    struct Running :
        IRunning,
        ISuspended,
        state< Running, Active >
    {
    public:
        typedef mpl::list<
            transition< Events::Stop, Stopped >,
            transition< Events::Suspend, RunningSuspended > > reactions;
    
        Running(my_context inContext) :
            my_base(inContext),
            IRunning(true),
            ISuspended(false)
        {
            context< Active >().Start();
        }
            
        ~Running(void)
        {
            context< Active >().Stop();
        }
    };

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