Boost logo

Boost Users :

From: Felipe Magno de Almeida (felipe.m.almeida_at_[hidden])
Date: 2008-06-24 00:37:53

On Mon, Jun 23, 2008 at 6:18 PM, Andreas Huber
<ahd6974-spamboostorgtrap_at_[hidden]> wrote:
> "Felipe Magno de Almeida" <felipe.m.almeida_at_[hidden]> wrote in message


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


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


> 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


Felipe Magno de Almeida

Boost-users list run by williamkempf at, kalb at, bjorn.karlsson at, gregod at, wekempf at