[MSM] Unable to retrieve correct value of attribute of a state from within actions. (Possible Bug?)

Hi everyone, Hi Christophe, Using Boost.MSM with eUML I tried to access the attribute of a state from within an action. However, the value is not what I expected when accessing the attribute of the state given as argument to the action. (It seems to be the default-parameter.) If I access the state directly (by name) and retrieve the value from it, it is set correctly as expected. Could it be that an action gets its arguments as copies? If so, I guess, the attributes of the copied arguments should be copied, too. This is the case for events. The attributes of an event given to the action seems to be set correctly. But the attributes of a state seem to be default-initialized. For clarification a simple example (this time tested and successfully compiled): #include <iostream> #define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS #include <boost/msm/back/state_machine.hpp> #include <boost/msm/front/euml/euml.hpp> using namespace boost::msm::front::euml; namespace msm = boost::msm; namespace mpl = boost::mpl; using namespace std; namespace { // The attribute: BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int, Number) BOOST_MSM_EUML_ATTRIBUTES((attributes_ << Number), NumberAttr) // An event containing the attribute: BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(event1, NumberAttr) // Two states with attributes: BOOST_MSM_EUML_STATE((no_action, no_action, attributes_ << Number), state1) BOOST_MSM_EUML_STATE((no_action, no_action, attributes_ << Number), state2) // Entry-action which initializes the attributes: BOOST_MSM_EUML_ACTION(init_events_and_states) { template <class Evt, class Fsm, class State> void operator()(Evt const&, Fsm&, State&) { event1.get_attribute(Number) = 100; state1.get_attribute(Number) = 1; state2.get_attribute(Number) = 2; }; }; // Action that prints the Number-attributes of event and state. // NOTE: The state-Numbers differ! BOOST_MSM_EUML_ACTION(printNumber) { template <class Fsm, class Evt, class SourceState, class TargetState> void operator()(Evt const& evt, Fsm&, SourceState& source, TargetState& target) { std::cout << "In action \"printNumber\"." << "\n event1.Number == " << event1.get_attribute(Number) << "\n evt.Number == " << evt.get_attribute(Number) << "\n state1.Number == " << state1.get_attribute(Number) << "\n source.Number == " << source.get_attribute(Number) << "\n state2.Number == " << state2.get_attribute(Number) << "\n target.Number == " << target.get_attribute(Number) << std::endl; }; }; // The transition table: BOOST_MSM_EUML_TRANSITION_TABLE(( state1 + event1 / printNumber == state2 ), stt) // The FSM front-end and back-end: BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( stt, init_ << state1, init_events_and_states ), FsmFrontend); typedef msm::back::state_machine<FsmFrontend> Fsm; void test() { Fsm fsm; // needed to start the highest-level FSM. // This initializes all attributes. fsm.start(); // emit event1 fsm.process_event(event1); } } // namespace int main() { test(); return 0; } // This prints the following (except "//") on the cmdline : // In action "printNumber". // event1.Number == 100 // evt.Number == 100 // state1.Number == 1 // source.Number == 0 // state2.Number == 2 // target.Number == 0 As you can see, the attribute-values of "event1" and "evt" are the same. However, the attribute-values of "state1" and "source" differ although they should be the same, too. (The same applies to "state2" and "target".) I tried this example with a custom type as attribute-type which prints each constructor-call and assignment-call and I realized, the copy-constructor was never called. If you want to try it yourself, just replace the declaration of the attribute in the above example with the following code: class MyInt { int val_; public: MyInt() : val_(50) { std::cout << "MyInt()" << std::endl; } MyInt(const MyInt& obj) : val_(obj.val_) { std::cout << "MyInt(const MyInt&) - val_=" << val_ << std::endl; } MyInt(const int& i) : val_(i) { std::cout << "MyInt(const int&) - val_=" << val_ << std::endl; } MyInt& operator=(const MyInt& obj) { val_ = obj.val_; std::cout << "op=(const MyInt&) - val_=" << val_ << std::endl; return *this; } MyInt& operator=(const int& i) { val_ = i; std::cout << "op=(const int&) - val_=" << val_ << std::endl; return *this; } }; // The attribute: BOOST_MSM_EUML_DECLARE_ATTRIBUTE(MyInt, Number) This looks like a bug, but I am not sure. Maybe I have to write my transition-table differently? Ciao, Deniz PS: I am using Boost 1.53.0 with GCC 4.7.2 (and C++11 support activated, most of the time.)

Hi Deniz,
Using Boost.MSM with eUML I tried to access the attribute of a state from within an action. However, the value is not what I expected when accessing the attribute of the state given as argument to the action. (It seems to be the default-parameter.) If I access the state directly (by name) and retrieve the value from it, it is set correctly as expected.
Could it be that an action gets its arguments as copies?
No, unless there is a recent bug, but I really don't think so. Copies in MSM are something we want to avoid for performance reasons.
// The attribute: BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int, Number) BOOST_MSM_EUML_ATTRIBUTES((attributes_ << Number), NumberAttr) // An event containing the attribute: BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(event1, NumberAttr) // Two states with attributes: BOOST_MSM_EUML_STATE((no_action, no_action, attributes_ << Number), state1) BOOST_MSM_EUML_STATE((no_action, no_action, attributes_ << Number), state2)
// Entry-action which initializes the attributes: BOOST_MSM_EUML_ACTION(init_events_and_states) { template <class Evt, class Fsm, class State> void operator()(Evt const&, Fsm&, State&) { event1.get_attribute(Number) = 100; state1.get_attribute(Number) = 1; state2.get_attribute(Number) = 2; }; };
Ouch! I admit I didn't think about this use case. This is what happens when an author tries to hide implementation details ;-) event1, state1 and state2 are really just "lies". eUML is simply some syntactic sugar on top of the functor front-end. These variables are only here because in the review, several people complained that you had to write in the stt something like State1() + Event1() == State2(). state1 simply is a dummy variable, all I want from it is its type. Of course, one instance exists, but normally, one does not care. So, there is no copy but msm takes the type of state1 and create its own instance (though you still have the possibility to copy from an already existing instance). What you are allowed to use are the template parameters: source state, target state, event, and fsm (on which, if you call fsm. template get_state<BOOST_MSM_EUML_STATE_NAME(state1)>() will give you a reference to the real state1). I'm not very happy with this. To replace some parens, I pay with more macros. If I could redo it, I'd not take this review recommendation. <snip code>
fsm.process_event(event1); // this really is event1!
As you can see, the attribute-values of "event1" and "evt" are the same. However, the attribute-values of "state1" and "source" differ although they should be the same, too. (The same applies to "state2" and "target".)
As you process event1, you really are changing the dummy instance, so you get the same as if you used the event in the action. Normally, you shouldn't care because events are temporary variables. I don't recommend using this trick ;-) States differ, as explained above. I hope I could clarify this. Cheers, Christophe

Hi Christophe,
// The attribute: BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int, Number) BOOST_MSM_EUML_ATTRIBUTES((attributes_ << Number), NumberAttr) // An event containing the attribute: BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(event1, NumberAttr) // Two states with attributes: BOOST_MSM_EUML_STATE((no_action, no_action, attributes_ << Number), state1) BOOST_MSM_EUML_STATE((no_action, no_action, attributes_ << Number), state2)
// Entry-action which initializes the attributes: BOOST_MSM_EUML_ACTION(init_events_and_states) { template <class Evt, class Fsm, class State> void operator()(Evt const&, Fsm&, State&) { event1.get_attribute(Number) = 100; state1.get_attribute(Number) = 1; state2.get_attribute(Number) = 2; }; };
Ouch! I admit I didn't think about this use case. This is what happens when an author tries to hide implementation details ;-) event1, state1 and state2 are really just "lies". eUML is simply some syntactic sugar on top of the functor front-end. These variables are only here because in the review, several people complained that you had to write in the stt something like State1() + Event1() == State2(). state1 simply is a dummy variable, all I want from it is its type. Of course, one instance exists, but normally, one does not care. [snip] So, there is no copy but msm takes the type of state1 and create its own instance (though you still have the possibility to copy from an already existing instance).
Thank you, that clarifies a lot.
What you are allowed to use are the template parameters: source state, target state, event, and fsm (on which, if you call fsm. template get_state<BOOST_MSM_EUML_STATE_NAME(state1)>() will give you a reference to the real state1).
You are missing a '&' or '*' behind the macro, but you are right, that does what I wanted in the first place.
fsm.process_event(event1); // this really is event1!
[snip] As you process event1, you really are changing the dummy instance, so you get the same as if you used the event in the action. Normally, you shouldn't care because events are temporary variables. I don't recommend using this trick ;-)
Just for final clarification three more questions: 1. You mean, that from within the "operator()" of an action I am only allowed to use the parameters for accessing states, events, and the FSM internals? (Neither "event_", "source_", "target_", "fsm_", nor accessing the dummy-instances directly.) This is now clear for the current event, current source- and target state as well as for all other states of the FSM (by accessing them with "get_state"). 2. However, how to access other events than the current one? It seems to work as I did before (by accessing the "dummy"-instance of the event), but if you do not recommend it, maybe there is a better solution? (I need to access all events directly to initialize their event-number attributes from within the FSM's entry-action.) 3. And all the underscore-accessors "event_", "source_", "target_", "fsm_" are only allowed to be used from within the transition table? Maybe you could emphasize the real usage in the documentation, so that nobody misuses it (like I did). Thanks, Deniz

Hi Deniz,
Just for final clarification three more questions:
1. You mean, that from within the "operator()" of an action I am only allowed to use the parameters for accessing states, events, and the FSM internals? (Neither "event_", "source_", "target_", "fsm_", nor accessing the dummy-instances directly.)
I didn't say this, just the dummy states you declare for the table. If you had this declared: BOOST_MSM_EUML_METHOD(ActivateEmpty_ , activate_empty , activate_empty_ , void , void ) You could call inside a state action something like: activate_empty_(target_)(evt,fsm,*this); or inside an action: activate_empty_(target_)(evt,fsm,src,target); But the interest is quite limited. They are made for the transition table. But as you see, msm creates some valid functors.
This is now clear for the current event, current source- and target state as well as for all other states of the FSM (by accessing them with "get_state").
2. However, how to access other events than the current one?
It seems to work as I did before (by accessing the "dummy"-instance of the event), but if you do not recommend it, maybe there is a better solution? (I need to access all events directly to initialize their event-number attributes from within the FSM's entry-action.)
Well, you could do this for events. Though as you seem to have a number for every type, a static member could be easier. The normal way is usually in the event constructor but I agree that for eUML, it is not so practical, so yes, your trick will do.
3. And all the underscore-accessors "event_", "source_", "target_", "fsm_" are only allowed to be used from within the transition table?
They are allowed everywhere but their main reason of existence is to make a nicer transition table.
Maybe you could emphasize the real usage in the documentation, so that nobody misuses it (like I did).
Ok, will do. Thanks. Cheers, Christophe

Hi Christophe,
1. You mean, that from within the "operator()" of an action I am only allowed to use the parameters for accessing states, events, and the FSM internals? (Neither "event_", "source_", "target_", "fsm_", nor accessing the dummy-instances directly.)
I didn't say this, just the dummy states you declare for the table. If you had this declared:
BOOST_MSM_EUML_METHOD(ActivateEmpty_ , activate_empty , activate_empty_ , void , void )
You could call inside a state action something like:
activate_empty_(target_)(evt,fsm,*this);
or inside an action:
activate_empty_(target_)(evt,fsm,src,target);
But the interest is quite limited. They are made for the transition table. But as you see, msm creates some valid functors.
It seems I was a little unclear with formulating my question. I assumed using "event_", "source_", "target_" and "fsm_" for accessing an attribute from within an action is not allowed. However, there seems to be no real reason for using them in this way, because one could as well use the corresponding action-parameter and its "get_attribute" member-function.
This is now clear for the current event, current source- and target state as well as for all other states of the FSM (by accessing them with "get_state").
2. However, how to access other events than the current one?
It seems to work as I did before (by accessing the "dummy"-instance of the event), but if you do not recommend it, maybe there is a better solution? (I need to access all events directly to initialize their event-number attributes from within the FSM's entry-action.)
Well, you could do this for events. Though as you seem to have a number for every type, a static member could be easier. The normal way is usually in the event constructor but I agree that for eUML, it is not so practical, so yes, your trick will do.
You are right. I would have preferred to use the event's constructor, but on the other side, I also prefer using your macros. :-) But ok, for events that does not seem to be a problem.
3. And all the underscore-accessors "event_", "source_", "target_", "fsm_" are only allowed to be used from within the transition table?
They are allowed everywhere but their main reason of existence is to make a nicer transition table.
And using them for accessing attributes seems to work only reliably in the transition table (at least for states). But that's fine, as one can use "get_attribute" from within actions.
Maybe you could emphasize the real usage in the documentation, so that nobody misuses it (like I did).
Ok, will do. Thanks.
Thank you a lot for all this clarification and especially for this great library. It helps me a lot. Ciao, Deniz
participants (2)
-
Christophe Henry
-
Deniz Bahadir