Boost logo

Boost Users :

Subject: Re: [Boost-users] [MSM] How to access event-attributes through a"kleene"-event?
From: Deniz Bahadir (deniz.bahadir_at_[hidden])
Date: 2013-02-21 06:42:07


Hi Christophe,

[snip]

>> 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.

You are right. That it is not exactly, what I had in mind. Especially,
with my second question I meant, if it might be possible to access the
event's attribute directly from the guard or action list without writing
an extra action for it (as I showed in my example or) as in the
following example:

// The transitions with kleene-event failing.
// Only the second transition works/compiles.
BOOST_MSM_EUML_TRANSITION_TABLE((
   State1 + kleene
            [event_(EventNo) > Int_<7>()] == State2,
   State1 + event1
            / (fsm_(lastEventNo) = event_(EventNo)) == State3
   State2 + kleene
            / (fsm_(lastEventNo) = event_(EventNo)) == State3
), stt)

>> 3. If there is not a way, is it planned to be implemented (if
>> possible) and when?

[snip]

>> // 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.

That is a funny coincidence. On the same evening after I wrote you my
first email I heard a talk about Boost.TypeErasure. :-) Quite
interesting library.

> 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;
> };
> }}

[snip]

> 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).

You seem to be right. I tried a long time to get this to work with the
any_event getting a method "get_attribute" (which "event_(attribute)"
seems to call). However, the compiler complains about being unable to do
proper type-deduction. But maybe, it is possible, but needs a more
experiences meta-template-programmer than me. :-)

However, I created some helper-Macros (almost identical to your
"BOOST_MSM_EUML_EVENT" and "BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES"
macros), and some helper actions (similar to your "Int_" action) which
allow me to create and access the (event-number) attributes almost as I
tried before.
Here they are:

// Macro which is identical to BOOST_MSM_EUML_EVENT but with
// additional parameter "members_list" which are all additional
// members surrounded by one pair of parentheses.
// The members from "members_list" will be inserted verbatim!
#define MY_BOOST_MSM_EUML_EVENT_WITH_MEMBERS(instance_name, \
                                              members_list) \
   struct instance_name ## _helper : \
     msm::front::euml::euml_event<instance_name ## _helper> \
   { \
     instance_name ## _helper(){} \
     BOOST_PP_EXPAND members_list \
     instance_name ## _helper const& operator()() const \
       {return *this;} \
   }; \
   static instance_name ## _helper instance_name;

// Macro which is identical to
// BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES but with additional
// parameter "members_list" which are all additional members
// surrounded by one pair of parentheses.
// The members from "members_list" will be inserted verbatim!
#define \
MY_BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES_AND_MEMBERS( \
         instance_name, attributes_name, members_list) \
   struct instance_name ## _helper : \
     msm::front::euml::euml_event<instance_name ## _helper>, \
     public attributes_name \
   { \
     template <class T,int checked_size> struct size_helper \
     { \
       typedef typename ::boost::mpl::less_equal< \
         typename ::boost::fusion::result_of::size<T>::type, \
         ::boost::mpl::int_<checked_size> >::type type; \
     }; \
     BOOST_PP_CAT(instance_name,_helper()) : \
       attributes_name(){} \
     typedef attributes_name::attributes_type attribute_map; \
     typedef ::boost::fusion::result_of:: \
       as_vector<attribute_map>::type attribute_vec; \
     BOOST_PP_REPEAT_FROM_TO(1,BOOST_PP_ADD( \
       FUSION_MAX_MAP_SIZE ,1), \
       MSM_EUML_EVENT_HELPER_CONSTRUCTORS, \
      (instance_name,attributes_name)) \
     BOOST_PP_REPEAT_FROM_TO(1,BOOST_PP_ADD( \
       FUSION_MAX_MAP_SIZE ,1), \
       MSM_EUML_EVENT_INSTANCE_HELPER_ATTRIBUTE_MAP, ~) \
     BOOST_PP_CAT(instance_name,_helper) operator()(){ \
       return BOOST_PP_CAT(instance_name,_helper)();} \
     BOOST_PP_REPEAT_FROM_TO(1,BOOST_PP_ADD( \
       FUSION_MAX_MAP_SIZE ,1), \
       MSM_EUML_EVENT_INSTANCE_HELPER_OPERATOR_IMPL, \
       instance_name) \
     BOOST_PP_EXPAND members_list \
   }; \
   static instance_name ## _helper instance_name;

// Action that checks if event-number is "greater than" the
// number provided as template-argument.
template <int Val>
struct IsEventNumberGT_ : euml_action<IsEventNumberGT_<Val> >
{
     using euml_action<IsEventNumberGT_<Val> >::operator=;
     typedef ::boost::mpl::int_<Val> value_type;
     enum {value = Val};

     template <class EVT,class FSM,
               class SourceState,class TargetState>
     bool operator()(EVT const& evt, FSM&,
                     SourceState&, TargetState&)
     {
         return (evt.getNumber() > Val);
     }
     template <class Event,class FSM,class STATE>
     bool operator()(Event const& evt, FSM&, STATE& )
     {
         return (evt.getNumber() > Val);
     }
};
// Todo: Make similar actions for "greater or equal",
// "lesser than", "lesser or equal" and "equal".

// An action which returns the event's number.
BOOST_MSM_EUML_ACTION(GetEventNumber_)
{
   template <class Fsm, class Evt,
            class SourceState, class TargetState>
   int operator()(Evt const& evt, Fsm&,
                 SourceState&, TargetState&)
   {
     return evt.getNumber();
   }
};

Both macros are identical to the original pendants except for the
additional parameter "members_list" and the line expanding this argument
"BOOST_PP_EXPAND members_list".

Now I am able to define and use events as in the following example:

// 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,
   (int getEventNumber() const {
      return get_attribute(EventNumber);
    }) // <-- members_list
)
/* ... more events with same scheme ... */

// Creating an instance of the new "kleene"-event which has
// a "getNumber" member-function.
any_number_event number_event;

// The transitions table
BOOST_MSM_EUML_TRANSITION_TABLE((
   State1 + number_event
            [IsEventNumberGT_<7>()] == State2,
   State1 + event1
            / (fsm_(lastEventNo) = event_(EventNo)) == State3
   State2 + kleene
            / (fsm_(lastEventNo) = GetEventNumber_)) == State3
), stt)

Now, all three transitions work, not only the second. Sadly it is not as
elegant as I hoped, but maybe it helps you in improving this great
library. :-)
Now I hope, Boost.TypeErasure will be released soon, so that you could
improve your library with Boost.TypeErasure functionality.

Thanks again for this great library,
Deniz


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