Boost logo

Boost Users :

Subject: Re: [Boost-users] [MSM] How to access event-attributes through a"kleene"-event?
From: Christophe Henry (christophe.j.henry_at_[hidden])
Date: 2013-02-19 17:01:13


Hi,

> Hello everyone,
>
> I am trying to use Boost.MSM with the eUML front-end and I must say: That
> is a pretty cool thing! :-)

thanks :)

> However, I stumbled across some points that raised several questions,
> which I hoped someone of you could answer.
> One of these points is the usage of the "kleene" event (with eUML). The
> questions that came to mind are the following:
>
>
> 1. Is there a way to access an attribute of an (original) event if the
> transition-table uses the "kleene" event?

Yes. As the doc states, a kleene event is a boost::any. You can use what
boost::any offers. type(), which is used as an example, is a member of
boost::any. The restriction is that you need to ask boost::any for a
concrete type to get it back.

> 2. If there is a way, can I access it directly from the guard or
> actions-list in the transition-table?

Sure. For example:

BOOST_MSM_EUML_ACTION(pause_playback)
    {
        template <class FSM,class EVT,class SourceState,class TargetState>
        void operator()(EVT const& evt,FSM&,SourceState& ,TargetState& )
        {
            cout << "player::pause_playback" << endl;
            std::cout << "event type: " << typeid(EVT).name() << std::endl;
            std::cout << "pause_playback with any event: " <<
evt.type().name() << std::endl;
        }
    };

The event is the EVT parameter, behind is hiding your kleene, and with it a
boost::any.
But I suspect it won't be enough for you, see below for more.

> 3. If there is not a way, is it planned to be implemented (if possible)
> and when?
>
>
> For better understanding I insert here a simple example which explains
> what I am trying to do:
>
> In my special case, all my events have an attribute "EventNo" which is a
> unique number for each event(-type). Now, I need a transition for the
> events with "EventNo" greater than a special number (e.g. 7). So I
> thought, I could do something like this:
>
>
> // The attribute:
> BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int, EventNo)
> BOOST_MSM_EUML_ATTRIBUTES((attributes_ << EventNo), EventNoAttr)
> // All events that contain this attribute:
> BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(event1, EventNoAttr)
> BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(event2, EventNoAttr)
> /* ... */
> BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(eventN, EventNoAttr)
>
> // Entry-action of FSM which initializes these event-numbers:
> BOOST_MSM_EUML_ACTION(init_all_events)
> {
> template <class Evt, class Fsm, class State>
> void operator()(Evt const&, Fsm&, State&)
> {
> event1.get_attribute(EventNo) = 1;
> event2.get_attribute(EventNo) = 2;
> /* ... */
> eventN.get_attribute(EventNo) = N;
> };
> }
>
> // Some states:
> BOOST_MSM_EUML_STATE((), State1)
> BOOST_MSM_EUML_STATE((), State2)
>
> // The transition table which contains all the logic:
> BOOST_MSM_EUML_TRANSITION_TABLE((
> State1 + kleene
> [event_(EventNo) > Int_<7>()] // <-- Not working!
> == State2,
> State1 + event1 == State3
> /* event2 to event7 are ignored! */
> ), stt)
>
> // The FSM front-end and back-end:
> BOOST_MSM_EUML_DECLARE_STATE_MACHINE((
> stt, init_ << init_all_events
> ), FsmFrontend;
> typedef msm::back::state_machine<FsmFrontend> Fsm;
>
>
>
> However, the expression "event_(EventNo)" in the transition-guard fails to
> compile with the following error:
>
> error: no match for call to
> ‘(boost::msm::front::euml::GetEvent_<EventNo_>) (const
> boost::msm::front::euml::kleene_&, boost::msm::back::state_machine<
> boost::msm::front::euml::func_state_machine<FsmFrontendtag, ...’
>
>
> I read in the documentation of the Functor front-end that I should use
> boost::any::type() to access the original event in transition actions, but
> I do not really know how to go on with it. I am also not sure if this
> applies to the eUML front-end at all (especially in the transition-guard).

Ah this is already becoming more "fun" :)
No, you don't have to use type(), it was just an example. With the known
limitations of boost::any, all you can do is call any's any_cast on your
event, check if it is one of your eventXXX, then you have the original event
and you can call its members.
It's not particularly beautiful but it's all what boost officially has to
offer.

Note that I insist on "officially". Let's suppose you'd have a look on the
excellent, accepted but not in any boost release, type_erasure, now the
situation looks better.

Disclaimer: this was not documented because I hadn't tried it out yet. I
just tried now quickly because I suspect you'll need it but there might be
some bugs.
This being said, please attach your seat belt, it might get bumpy :)

You can define your type_erasure's any with extra members and use them in
your guards and actions.

BOOST_TYPE_ERASURE_MEMBER((has_getNumber), getNumber, 0);
//type erasure event
typedef ::boost::mpl::vector<
    has_getNumber<int(), const boost::type_erasure::_self>,
    boost::type_erasure::relaxed_match,
    boost::type_erasure::copy_constructible<>,
    boost::type_erasure::typeid_<>
> any_number_event_concept;
struct any_number_event :
boost::type_erasure::any<any_number_event_concept>,
                                             msm::front::euml::euml_event<any_number_event>
{
    template <class U>
    any_number_event(U const& u):
boost::type_erasure::any<any_number_event_concept> (u){}
    any_number_event(): boost::type_erasure::any<any_number_event_concept>
(){}
};

All you still need to do is tell MSM about your own, brand new kleene event
as explained here:
http://svn.boost.org/svn/boost/trunk/libs/msm/doc/HTML/ch03s03.html#any-event

namespace boost { namespace msm{
    template<>
    struct is_kleene_event< any_number_event >
    {
      typedef boost::mpl::true_ type;
    };
}}

Now let's try and modify some standard example:
We need an instance for the table and a transition:

any_number_event number_event;

Playing + number_event / pause_playback == Paused //
replaces pause as event

Ok, now let's make our events more powerful:

    struct play_impl : msm::front::euml::euml_event<play_impl> {int
getNumber()const {return 0;}};
    struct end_pause_impl : msm::front::euml::euml_event<end_pause_impl>{int
getNumber()const {return 1;}};
    struct stop_impl : msm::front::euml::euml_event<stop_impl>{int
getNumber()const {return 2;}};
    struct pause_impl : msm::front::euml::euml_event<pause_impl>{int
getNumber()const {return 3;}};
    struct open_close_impl :
msm::front::euml::euml_event<open_close_impl>{int getNumber()const {return
4;}};
    struct cd_detected_impl :
msm::front::euml::euml_event<cd_detected_impl>{int getNumber()const {return
5;}};

An action becomes:

    BOOST_MSM_EUML_ACTION(pause_playback)
    {
        template <class FSM,class EVT,class SourceState,class TargetState>
        void operator()(EVT const& evt,FSM&,SourceState& ,TargetState& )
        {
            cout << "player::pause_playback" << endl;
            std::cout << "event type: " << typeid(EVT).name() << std::endl;
            std::cout << "event's number: " << evt.getNumber() << std::endl;
// we expect 3 here, as our event is a pause event
        }
    };

Calls to process_event stay unchanged.

Granted, this is already quite advanced but AFAIK there is no other way.

There is however a catch: I don't know if all this is possible with the full
eUML as you use it. I'm afraid you'll have to do as in my example and go for
the almost eUML (no event attributes, classical events).

I also realize that a complete example cannot hurt, so I just added them in
trunk (rev 83038) in doc/HTML/examples
(SimpleTutorialWithEumlTableKleene.cpp and
SimpleTutorialWithEumlTableTypeErasure.cpp)

Have fun,

Christophe


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