[statechart] Substates in dll
Is there any way to partition a state machine using boost::statechart such
that the main state machine is in one dll, and its substates (and possibly
their substates) are in separate dll's?
It would seem that this would create a cyclic dependency. Suppose you have
e.g. a simple statechart called MyStatechart containing an initial state
State1 which transitions to State2 in response to some event. Is it possible
to implement MyStatechart and State1 in MyStatechart.dll, and State2 in
State2.dll?
Since the code for State1 will need to reference State2 (in order to
transition to it), MyStatechart.dll will have a dependency on State2.dll.
But since State2 is contained in MyStatechart, its type will have to look
something like
struct State2: sc::state
"Bill Clark"
Yes, that example shows how you can put different states in different
translation units. In that case, though, the cyclic dependency is worked
around by using forward references. This works because everything is in a
single component, so the linker can see everything at once. If you link the
states separately, though, there's no order that can work: Since
MyStatechart.dll requires State2.dll (really its export lib) in order to
link, State2 has to be built before MyStatechart. But since State2.dll
requires MyStatechart.dll (really its export lib) in order to link,
MyStatechart has to be built before State2.
We can work around this by using static libs throughout, but that is much
less desirable for our architecture. That's why I'm wondering whether I'm
missing some technique for breaking the cyclic dependency at link time.
On Fri, Jul 10, 2009 at 12:43 PM, Commander Pirx
"Bill Clark"
schrieb im Newsbeitrag news:179b0d0d0907090954v5713336j2a6577f4c354fcde@mail.gmail.com... Is there any way to partition a state machine using boost::statechart such that the main state machine is in one dll, and its substates (and possibly their substates) are in separate dll's? It would seem that this would create a cyclic dependency. Suppose you have e.g. a simple statechart called MyStatechart containing an initial state State1 which transitions to State2 in response to some event. Is it possible to implement MyStatechart and State1 in MyStatechart.dll, and State2 in State2.dll?
Since the code for State1 will need to reference State2 (in order to transition to it), MyStatechart.dll will have a dependency on State2.dll. But since State2 is contained in MyStatechart, its type will have to look something like
struct State2: sc::state
{ //... }; which means that State2.dll will have a dependency on MyStatechart.dll.
Is it possible to avoid this cycle?
Thanks for any help, Bill
There is a topic in the statechart tutorial http://www.boost.org/doc/libs/1_39_0/libs/statechart/doc/tutorial.html#Sprea... Which covers a similiar topic. Perhaps you can modify your architecture and work with nested state machines.
Pirx
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
Hi Bill
Is there any way to partition a state machine using boost::statechart such that the main state machine is in one dll, and its substates (and possibly their substates) are in separate dll's?
Yes, that's possible, with one restriction: The inner initial state of each state must be in the same dll as the state itself, see below.
It would seem that this would create a cyclic dependency.
The cyclic dependency only exists between the state and its inner initial state. The state does not need to have any knowledge of all its other inner states.
Suppose you have e.g. a simple statechart called MyStatechart containing an initial state State1 which transitions to State2 in response to some event. Is it possible to implement MyStatechart and State1 in MyStatechart.dll, and State2 in State2.dll?
It seems this should be possible. A prerequisite is that the transition from State1 to State2 must not be implemented with an ordinary sc::transition<> but with a sc::custom_reaction<>. By doing that, you are able to move the knowledge of the target state from the declaration (hpp file) to an ordinary function (which can reside in a cpp file), see http://www.boost.org/doc/libs/1_39_0/libs/statechart/doc/tutorial.html#Sprea... for more information. Now, in order to really break the cycle, the *implementation* of State1::react should reside in State2.dll. Don't know whether that suits your needs, if not feel free to follow-up.
Since the code for State1 will need to reference State2 (in order to transition to it), MyStatechart.dll will have a dependency on State2.dll. But since State2 is contained in MyStatechart, its type will have to look something like
struct State2: sc::state
{ //... }; which means that State2.dll will have a dependency on MyStatechart.dll.
No, IIUC. It should be enough when MyStatechart.dll and State2.dll include the same header, e.g. MyStatechart.hpp. You might want to have a look at the TuTest*.* files in the test directory. These do not implement your use case, but at least illustrate how the importing/exporting required by some platforms can be done. HTH, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.
From what I can tell, the cyclic dependency is a consequence of the fact
Hi Andreas, I'm afraid I'm still not seeing a way to accomplish this (which could easily mean that I'm missing something). So taking the TuTest code as an starting point, here's what I would like to be able to do: - Put TuTest.cpp and TuTest.hpp in a dll, TuTest.dll, called by the main program. Keep the initial transition in the cpp file, as in the test code. - Make the Initial state react to EvX by transitioning to a new state, call it MyState, defined in MyState.dll. MyState, in turn, would have its own internal transitions and states, which it hides from clients by putting them in its own cpp files. In your reply you had said that the state dll, MyState.dll in this case, won't have a link-time dependency on the state machine; it need only include its header. This would be true if the state machine were implemented entirely in the header. However, the header won't be enough if its initial transition is hidden in a cpp file; the state would need to link with the state machine. And similarly, in the state machine's cpp, it will be necessary to link to MyState in order to transition to it. Again, we could avoid this if MyState were implemented entirely in its header, but we want to hide its details inside its cpp files. As far as I've been able to tell, the same cyclic relationship would appear no matter how we moved the functions around, e.g. by moving the react function that takes us from Initial to MyState into MyState.dll. This would also seem to cause error messages about inconsistent dll linkage, but perhaps there are ways around that. I didn't think it was possible to split the implementation of a class across multiple dlls. Even so, I don't see how we could avoid dependencies in both directions. that a state's type is parameterized on both its immediate outer state and its initial inner state. It's possible to use forward references to make the compiler happy with this, and if you're using static libraries there's no problem. But if you try to split out parts of a state machine into separately loadable components (dlls or shared objects), the linker will necessarily be unable to resolve things in one direction or the other. I hope the above is reasonably clear, and thanks for your assistance - I really hope that there's something I've overlooked or misunderstood! Bill On Sat, Jul 11, 2009 at 2:18 AM, Andreas Huber < ahd6974-spamboostorgtrap@yahoo.com> wrote:
Hi Bill
Is there any way to partition a state machine using boost::statechart such
that the main state machine is in one dll, and its substates (and possibly their substates) are in separate dll's?
Yes, that's possible, with one restriction: The inner initial state of each state must be in the same dll as the state itself, see below.
It would seem that this would create a cyclic dependency.
The cyclic dependency only exists between the state and its inner initial state. The state does not need to have any knowledge of all its other inner states.
Suppose you have
e.g. a simple statechart called MyStatechart containing an initial state State1 which transitions to State2 in response to some event. Is it possible to implement MyStatechart and State1 in MyStatechart.dll, and State2 in State2.dll?
It seems this should be possible. A prerequisite is that the transition from State1 to State2 must not be implemented with an ordinary sc::transition<> but with a sc::custom_reaction<>. By doing that, you are able to move the knowledge of the target state from the declaration (hpp file) to an ordinary function (which can reside in a cpp file), see
< http://www.boost.org/doc/libs/1_39_0/libs/statechart/doc/tutorial.html#Sprea...
for more information. Now, in order to really break the cycle, the *implementation* of State1::react should reside in State2.dll. Don't know whether that suits your needs, if not feel free to follow-up.
Since the code for State1 will need to reference State2 (in order to
transition to it), MyStatechart.dll will have a dependency on State2.dll. But since State2 is contained in MyStatechart, its type will have to look something like
struct State2: sc::state
{ //... }; which means that State2.dll will have a dependency on MyStatechart.dll.
No, IIUC. It should be enough when MyStatechart.dll and State2.dll include the same header, e.g. MyStatechart.hpp. You might want to have a look at the TuTest*.* files in the test directory. These do not implement your use case, but at least illustrate how the importing/exporting required by some platforms can be done.
HTH,
-- Andreas Huber
When replying by private email, please remove the words spam and trap from the address shown in the header.
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
So taking the TuTest code as an starting point, here's what I would like to be able to do:
- Put TuTest.cpp and TuTest.hpp in a dll, TuTest.dll, called by the main program. Keep the initial transition in the cpp file, as in the test code. - Make the Initial state react to EvX by transitioning to a new state, call it MyState, defined in MyState.dll. MyState, in turn, would have its own internal transitions and states, which it hides from clients by putting them in its own cpp files.
Ok.
In your reply you had said that the state dll, MyState.dll in this case, won't have a link-time dependency on the state machine; it need only include its header. This would be true if the state machine were implemented entirely in the header. However, the header won't be enough if its initial transition is hidden in a cpp file; the state would need to link with the state machine.
Right, my bad. After some reflection, it seems that your assessment is correct. You simply cannot push only some states of a state machine into dlls without introducing a circular dependency between binaries. However, it seems you can circumvent this relatively easily, as follows (UNTESTED): *** MyStatechartBase.hpp (included from cpps compiled into both dlls) *** // Depending on your platform, types/functions in this header might need // to be properly exported/imported // ... struct EvWhatever : sc::event< EvWhatever > {}; struct State1; struct MyStatechartBase : sc::state_machine< MyStatechartBase, State1 > { virtual sc::result react( State1 & state1, const EvWhatever & ev ) = 0; }; struct State1 : sc::simple_state< State1, MyStatechartBase > { typedef sc::custom_reaction< EvWhatever > reactions; sc::result react( const EvWhatever & ev ) { return outermost_context().react( this, ev ); } }; *** MyStatechartBase.cpp (compiled into MyStatechartBase.dll) *** #include "MyStatechartBase.hpp" // ... *** MyStatechart.cpp (compiled into MyStatechart.dll) *** // This might have to be split into hpp/cpp #include "MyStatechartBase.hpp" // ... struct State2 : sc::simple_state< State2, MyStatechartBase > {}; struct MyStatechart : MyStatechartBase { virtual sc::result react( State1 & state1, const EvWhatever & ev ) { return state1.transit< State2 >(); } }; Of course, this has two (IMO minor) problems: 1) You cannot hide State1, it must be publicly visible 2) You have to subclass the state machine It seems this should do pretty much what you want? If not, please let me know. Regards, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.
That's interesting. What this does is to move the knowledge that "State1 transitions to State2 in response to EvWhatever" into a separate binary from the one where State1 is implemented. This does seem to address the basic problem. What would happen if we wanted this to scale to more states, so that e.g. there's a State3 that we transition to in response to EvAnother? And so on with more states and events, where we'd like each of State3, State4, etc. to be implemented in their own dll. It would seem that we'd have to build up a hierarchy where we have one inheritance level for each state. This could get cumbersome, but it would probably work. In any case, for our application, we may not actually require all this. We may be able to accomplish what we need by splitting out the functionality into independent state machines, rather than independent states within a single state machine. We'll need to analyze the kinds of coordination that would have to happen between machines, but at this point I believe we could use this approach. Thanks very much for your help. I have another (I hope simpler) question, which I'll put in a separate message. Bill On Mon, Jul 13, 2009 at 2:54 AM, Andreas Huber < ahd6974-spamboostorgtrap@yahoo.com> wrote:
So taking the TuTest code as an starting point, here's what I would like
to be able to do:
- Put TuTest.cpp and TuTest.hpp in a dll, TuTest.dll, called by the main program. Keep the initial transition in the cpp file, as in the test code. - Make the Initial state react to EvX by transitioning to a new state, call it MyState, defined in MyState.dll. MyState, in turn, would have its own internal transitions and states, which it hides from clients by putting them in its own cpp files.
Ok.
In your reply you had said that the state dll, MyState.dll in this case,
won't have a link-time dependency on the state machine; it need only include its header. This would be true if the state machine were implemented entirely in the header. However, the header won't be enough if its initial transition is hidden in a cpp file; the state would need to link with the state machine.
Right, my bad. After some reflection, it seems that your assessment is correct. You simply cannot push only some states of a state machine into dlls without introducing a circular dependency between binaries. However, it seems you can circumvent this relatively easily, as follows (UNTESTED):
*** MyStatechartBase.hpp (included from cpps compiled into both dlls) ***
// Depending on your platform, types/functions in this header might need // to be properly exported/imported
// ...
struct EvWhatever : sc::event< EvWhatever > {};
struct State1; struct MyStatechartBase : sc::state_machine< MyStatechartBase, State1 > { virtual sc::result react( State1 & state1, const EvWhatever & ev ) = 0; };
struct State1 : sc::simple_state< State1, MyStatechartBase > { typedef sc::custom_reaction< EvWhatever > reactions;
sc::result react( const EvWhatever & ev ) { return outermost_context().react( this, ev ); } };
*** MyStatechartBase.cpp (compiled into MyStatechartBase.dll) ***
#include "MyStatechartBase.hpp" // ...
*** MyStatechart.cpp (compiled into MyStatechart.dll) ***
// This might have to be split into hpp/cpp
#include "MyStatechartBase.hpp" // ...
struct State2 : sc::simple_state< State2, MyStatechartBase > {};
struct MyStatechart : MyStatechartBase { virtual sc::result react( State1 & state1, const EvWhatever & ev ) { return state1.transit< State2 >(); } };
Of course, this has two (IMO minor) problems: 1) You cannot hide State1, it must be publicly visible 2) You have to subclass the state machine
It seems this should do pretty much what you want? If not, please let me know.
Regards,
-- Andreas Huber
When replying by private email, please remove the words spam and trap from the address shown in the header.
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
[snip]
What would happen if we wanted this to scale to more states, so that e.g. there's a State3 that we transition to in response to EvAnother? And so on with more states and events, where we'd like each of State3, State4, etc. to be implemented in their own dll. It would seem that we'd have to build up a hierarchy where we have one inheritance level for each state.
Not necessarily. It is usually enough when you enable client code to supply one or more *parts* of a state machine, each part containing as many or as few states as necessary. As I showed in the last post, you can use virtual functions whenever you want to transit to a state of such a client-supplied part and make those states visible to which client-supplied parts need to make transitions. I personally haven't used this very much, but it seems inheritance hierarchies should stay rather shallow this way. This approach sort of resembles the template method pattern. Regards, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.
participants (3)
-
Andreas Huber
-
Bill Clark
-
Commander Pirx