
On Mon, Jun 23, 2008 at 6:18 PM, Andreas Huber <ahd6974-spamboostorgtrap@yahoo.com> wrote:
"Felipe Magno de Almeida" <felipe.m.almeida@gmail.com> wrote in message news:a2b17b60806211502i447c22e1t9d552dd76b8972a1@mail.gmail.com...
[snip]
IIUC the diagram, the functional factory solution allows both Y and Z to have arguments to its constructors. The Y state could pass the arguments to its base with the arguments for Z constructor, which would then create a factory to instantiate Z.
So you're suggesting that simple_state::simple_state() should be overloaded say 10 times, one templated overload for each distinct number of parameters?
Yes. This can be done very easily with boost.preprocessor. #ifndef BOOST_STATECHART_PARAMS #define BOOST_STATECHART_PARAMS 10 #endif #define BOOST_STATECHART_DETAIL_generate_overload(z, n, data) \ template <BOOST_PP_ENUM_PARAMS(n, typename A)> \ simple_state(BOOST_PP_ENUM_BINARY_PARAMS(n, A, a)) \ : boost::bind(boost::factory<InitialState*>(), BOOST_PP_ENUM_PARAMS(n, a)) \ {} BOOST_PP_REPEAT(SMTP_CLIENT_DETAIL_PARAMS, BOOST_STATECHART_DETAIL_generate_overload, ~) #undef BOOST_STATECHART_DETAIL_generate_overload Something like that, but taking care of the 0 version which is not handled above. If you find it too inconvenient, a simple fusion sequence works too. In my cppgui library I use a fusion sequence to allow passing argument constructors to windows: wnd<my_window> w = create<my_window>( _arguments = fusion::make_vector(a1, a2, a3) ); But since this lib uses boost.named_parameters, I can't pass multiple arguments to it, so a sequence is the only thing that fits. Maybe as syntactic sugar this could be done, instead of overloading simple_state constructor. template <typename T0> fusion::vector<T0> inner_state_arguments(T0); template <typename T0, typename T1> fusion::vector<T0, T1> inner_state_arguments(T0 a0, T1 a1); We could write: struct Z : sc::simple_state<Y, Z> { Z(int, int); }; struct Y : sc::simple_state<Y, X, Z> { Y() : sc::simple_state(inner_state_arguments(0,1)) {} }; And then simple_state could use a fusion sequence to create a factory. This transfers the overload to something else, but at least the intent it is more explicit.
If someone wants to pass arguments to X, then they should transit to X, and X transit to Y IMHO.
Transiting to X could be semantically different from transiting to Y. In the diagram discussed so far it's not but looking at the transitions triggered by e3 and e4 in ...
http://i.cmpnet.com/embedded/gifs/9901/9901feat1fig4.gif
... you'll see an example where it is. Transiting to S0 leads to the entry of S0 and S0_1 while transiting to S0_2 leads to the entry of S0 and S0_2.
I understand. But if someone is trying to transit directly to an inner state, does he care about the local-state of the new outer state? This is not rhetorical actually, I'm a little inexperienced with developing state machines. So far I've writed a SMTP client with asio, and am creating a SMTP server. I have no other experience with state machines.
I see your triggering_event idea, and I find it not very useful because of its necessary downcast and dynamic typing, it also seems to me that the reponsability to receive the arguments continue to be in the just-entered state. Which I find dissatisfying.
I wholeheartedly agree, but apart from your factory suggestion I've not yet seen any good ideas how this could be improved.
I hope I convince you of my idea then. :)
Even if we were to go the factory route (my version), you'd be forced to implement quite a few factory classes (I guess approximately one for each state ctor with parameters).
I don't think creating classes is a good idea. The constructors would still be fixed. I believe constructor overloading is of much importance in this. A simple construction indirection is all that's needed for this to work. [snip]
You could make your events visitable, in which case you could do with a finite number of casts/virtual function calls for any number of state ctors. Of course, this would require the implementation of a visitor class, which isn't exactly elegant either.
I believe I'd rather continue with the workaround I use today. sc::result react( event1 const& ) { state_machine& m = context<state_machine>(); sc::result r = transit<new_state>(); new_state const& s = m.state_cast<new_state const&>(); s.initialize(a1, a2); return r; } Even with the many pitfalls this has (everyday I forget to copy local-state I need to pass along to a automatic variable before calling transt<>) it is at least more straightforward.
Regards,
-- Andreas Huber
Regards, -- Felipe Magno de Almeida