diff --git a/doc/ExceptionsAndOrthogonalStates.gif b/doc/ExceptionsAndOrthogonalStates.gif new file mode 100644 index 0000000..b2d8691 Binary files /dev/null and b/doc/ExceptionsAndOrthogonalStates.gif differ diff --git a/doc/OrthogonalStates.gif b/doc/OrthogonalStates.gif new file mode 100644 index 0000000..3a47069 Binary files /dev/null and b/doc/OrthogonalStates.gif differ diff --git a/doc/ThrowingEntryAction.gif b/doc/ThrowingEntryAction.gif new file mode 100644 index 0000000..b0c97e0 Binary files /dev/null and b/doc/ThrowingEntryAction.gif differ diff --git a/doc/ThrowingInStateReaction.gif b/doc/ThrowingInStateReaction.gif new file mode 100644 index 0000000..cc7d214 Binary files /dev/null and b/doc/ThrowingInStateReaction.gif differ diff --git a/doc/ThrowingTransitionAction.gif b/doc/ThrowingTransitionAction.gif new file mode 100644 index 0000000..a3b9531 Binary files /dev/null and b/doc/ThrowingTransitionAction.gif differ diff --git a/doc/acknowledgments.html b/doc/acknowledgments.html index cb2a38b..7db7f09 100644 --- a/doc/acknowledgments.html +++ b/doc/acknowledgments.html @@ -1,7 +1,7 @@ - + The boost::fsm library - Acknowledgments @@ -27,8 +27,8 @@ the implementation of boost::fsm hugely benefit from Alekseys work. I would have
  • Aleksey Gurtovoy, Douglas Gregor and Jeff Garland for their encouragement to continue working on this library.

  • -

    Revised 07 April, 2003

    -

    © Copyright Andreas Huber 2003. All Rights Reserved.

    +

    Revised +19 May, 2003

    +

    © Copyright Andreas Huber Dönni 2003. All Rights Reserved.

    - + \ No newline at end of file diff --git a/doc/index.html b/doc/index.html index a672cd0..fa18360 100644 --- a/doc/index.html +++ b/doc/index.html @@ -1,7 +1,7 @@ - + The boost::fsm library - Overview @@ -52,8 +52,8 @@
    Acknowledgments

    -

    Revised 07 April, 2003

    -

    © Copyright Andreas Huber 2003. All Rights Reserved.

    +

    Revised +19 May, 2003

    +

    © Copyright Andreas Huber Dönni 2003. All Rights Reserved.

    - + \ No newline at end of file diff --git a/doc/rationale.html b/doc/rationale.html index 0078eef..a46bc3c 100644 --- a/doc/rationale.html +++ b/doc/rationale.html @@ -1,7 +1,7 @@ - + The boost::fsm library - Rationale @@ -24,7 +24,7 @@
    Why yet another state machine framework?
    State-local storage
    Dynamic configurability
    -
    Speed
    +
    Resource usage
    Determinism
    Error handling
    User actions: Member functions vs. function objects
    @@ -36,14 +36,14 @@

    boost::fsm should ...

    1. be fully type-safe. Any type mismatches should be flagged with an error at compile-time.
    2. -
    3. not require the use of a code generator. A lot of the existing fsm solutions force the developer to design the statemachine either +
    4. not require the use of a code generator. A lot of the existing FSM solutions force the developer to design the state machine either graphically or in a specialized language. All or part of the code is then generated.
    5. allow for easy transformation of a UML statechart (defined in UML - specifications) into a working statemachine. Vice versa, an existing C++ implementation of a state machine should be fairly trivial + specifications) into a working state machine. Vice versa, an existing C++ implementation of a state machine should be fairly trivial to transform into a UML statechart. Specifically, the following state machine features should be supported:
    6. produce a customizable reaction when a C++ exception is propagated from user code.
    7. -
    8. work with any threading library
    9. +
    10. support sequential and concurrent state machines and leave it to the + end-user which thread a concurrent state machine will run in.
    11. support the development of arbitrarily large and complex state machines. This means that multiple developers should be able to work on the same state machine simultaneously.
    12. allow the user to customize all resource management so that the library could be used for applications with hard real-time @@ -69,17 +70,18 @@ Fails to satisfy at least the requirements 2, 4, 5, 6, 8 (there is quite a bit of error checking before code generation, though).
    13. The framework accompanying the article "State Machine Design in C++"
      http://www.cuj.com/articles/2000/0005/0005f/0005f.htm?topic=articles
      -
      Fails to satisfy at least the requirements 1, 3, 4, 5 (there is no direct threading support), 6, 8.
    14. + Fails to satisfy at least the requirements 1, 3, 4, 5 (there is no direct threading support), + 6, 8.

      boost::fsm currently satisfies all requirements except for 3 (history not yet implemented) and 5 (support for threading not yet implemented).

      State-local storage

      This not yet widely known state machine feature is enabled by the fact that every state in boost::fsm is represented by a class. Upon -state-enrty, an object of the class is constructed and the object is later destructed when the state machine exits the state. Any data that +state-entry, an object of the class is constructed and the object is later destructed when the state machine exits the state. Any data that is useful only as long as the machine resides in the state can (and should) thus be a member of the state. This feature paired with the ability to spread a state machine over several translation units makes it possible to have multiple developers simultaneously working on the same state machine. Moreover, local changes to the machine layout no longer lead to recompilation of the whole machine. 

      -

      In most existing FSM frameworks the whole state machine runs in one global environment (context). That is, all resource handles and +

      In most existing FSM frameworks the whole state machine runs in one environment (context). That is, all resource handles and variables local to the state machine are stored in one place (normally as members of the class that also derives from some state machine base class). For large state machines this often leads to the class having a huge number of data members most of which are needed only briefly in a tiny part of the machine. The state machine class therefore often becomes a change hotspot what leads to frequent @@ -89,19 +91,18 @@ recompilations of the whole state machine. 

      -

      Why not use a dynamically configurable fsm library for all state machines?

      -

      One might argue that a dynamically configurable fsm framework is all one ever needs because any state machine can be implemented +

      Why not use a dynamically configurable FSM library for all state machines?

      +

      One might argue that a dynamically configurable FSM framework is all one ever needs because any state machine can be implemented with it. However, due to its nature such a framework has a number of disadvantages when used to implement static state machines:

      Error handling

      -

      There is not a single word about error handling in the UML state machine semantics specifications. Moreover, most existing fsm solutions +

      There is not a single word about error handling in the UML state machine semantics specifications. Moreover, most existing +FSM solutions also seem to ignore the issue. 

      -

      Why an fsm library should support error handling

      +

      Why an FSM library should support error handling

      Consider the following state configuration:

      -

      Both states define entry actions (x() and y()). So, whenever a transition leads to state A becoming current, a call to x() will -immediately be followed by a call to y(). y() could depend on the side-effects of x(). Therefore, executing y() does not make sense if x() -fails. This is not an esoteric corner case but happens in every-day state machines all the time. For example, x() could acquire memory the -contents of which is later modified by y(). This problem is very similar to failing base class constructors: First, the base class -constructor is called and then the derived class constructor. If the base class constructor fails (by throwing an exception) the -construction is aborted and the object never comes to life.

      -

      If an fsm framework does not account for failing actions, the user is forced to adopt cumbersome workarounds. For example, a failing -action could post an appropriate error event and set a global error variable to true. Every following action would then first check the -error variable before doing anything. After all actions have completed (by doing nothing!), the previously posted error event would be +

      Both states define entry actions (x() and y()). Whenever state A becomes +current, a call to x() will immediately be followed by a call to y(). y() could +depend on the side-effects of x(). Therefore, executing y() does not make sense +if x() fails. This is not an esoteric corner case but happens in every-day state +machines all the time. For example, x() could acquire memory the contents of +which is later modified by y(). There is a different but in terms of error handling equally critical situation +in the tutorial when Running::~Running accesses its outer state +Active. Had the entry action of Active failed and had Running been +entered anyway then Running's exit action would have invoked undefined behavior.
      +The error handling situation with outer and inner states resembles the one with +base and derived classes: If a base class constructor fails (by throwing an exception) the +construction is aborted, the derived class constructor is not called and the object never comes to life.

      +

      If an FSM framework does not account for failing actions, the user is forced to adopt cumbersome workarounds. For example, a failing +action would have to post an appropriate error event and set a global error variable to true. Every following action would first +have to check the +error variable before doing anything. After all actions have completed (by doing nothing!), the previously posted error event would +have to be processed what would lead to the remedy action begin executed. Please note that it is not sufficient to simply push the error event into the end of the queue as other events could still be pending. Instead, the error event has absolute priority and would have to be dealt with immediately.

      So, to be safe, programmers would have to encapsulate the code of every action in if ( !error ) { /* action */ } -blocks. Moreover, a try { /* action */ } catch ( ... ) { /* post error event */ error = true; } statement would have to be -added because letting an exception propagate out of a user action would at best terminate the state machine immediately. Adding all this +blocks. Moreover, a try { /* action */ } catch ( ... ) { /* post error event */ error = true; } statement would +often have to be +added because called functions might throw and letting an exception propagate out of a user action would at best terminate the state machine immediately. +Writing all this boiler-plate code is simply boring and quite unnecessary.

      Error handling support in boost::fsm

      -
      -

      In the last two cases the state-machine is not in a stable state during error handling and leaving it there (e.g. by ignoring the - exception event) would violate an invariant of state machines. So, the exception event handler must either make a transition or terminate - to bring the machine back into a stable state. That’s why the framework checks that the state machine is stable after processing an - exception event. If this is not the case the state machine is terminated and the exception rethrown.

      -

      User actions: Member functions vs. function objects

      -With boost::fsm, all user-supplied functions (event handlers, entry-, exit- and transition-actions) must be class members. The reasons for +With boost::fsm, all user-supplied functions (react functions, entry-, exit- and transition-actions) must be class members. The reasons for this are as follows:

      Memory management

      -

      Out of the box, boost::fsm employs a std::queue to store event pointers and uses a std::list for state storage. -Moreover, state objects are allocated through normal operator new. Allocations and deallocations only take place during state -transitions. The default memory management should be satisfactory for applications where all the following prerequisites are met:

      +

      Out of the box, boost::fsm allocates all internal data on the normal +heap. This should be satisfactory for applications where all the following prerequisites are met:

      -

      Note that the above prerequisites hold for almost all applications where all events are generated as a result of the interaction with one -user. Should a system not meet any of these prerequisites customization of all memory management (not just boost::fsm's) should be +

      Should a system not meet any of these prerequisites customization of all memory management (not just boost::fsm's) should be considered. This library supports this as follows:

      +

      simple_state and state subclass objects are constructed and destructed only +by the state machine. It would therefore be possible to use the state_machine +allocator instead of forcing the user to overload operator new and operator +delete. However, a lot of +systems employ at most one instance of a particular state machine, which means +that a) there is at most one object of a particular state and b) this object is +always constructed, accessed and destructed by one and the same thread. We can +exploit these facts in a much simpler (and faster) new/delete +implementation (for example, see UniqueObject.hpp in the BitMachine example). +However, this is only possible as long as we have the freedom to overload memory management for +every single state class.

      Double dispatch

      At the heart of every state machine lies an implementation of double dispatch. This is due to the fact that the incoming event and the current state define exactly which reaction the state machine will produce. machine.

    15. Two-dimensional array of function pointers: To satisfy requirement 6, it should be possible to spread a single boost::fsm state machine over several translation units. This however means, that the dispatch table must be filled at runtime and the different - translation units must somehow make themselves "known", so that their part of the statemachine can be added to the table. + translation units must somehow make themselves "known", so that their part of the state machine can be added to the table. There simply is no way to do this automatically and portably. So, the only portable way that a state machine distributed over several translation units could employ table-based double dispatch lies with the user. The programmer(s) would somehow have to manually tie together the various pieces of the state machine. Not only does this scale badly but is also quite error-prone.

    16. -

      Revised 07 April, 2003

      -

      © Copyright Andreas Huber 2003. All Rights Reserved.

      +

      Revised +19 May, 2003

      +

      Copyright © 2003 Andreas Huber Dönni. All Rights Reserved.

      - + \ No newline at end of file diff --git a/doc/tutorial.html b/doc/tutorial.html index b4004ec..0cd1914 100644 --- a/doc/tutorial.html +++ b/doc/tutorial.html @@ -1,7 +1,7 @@ - + The boost::fsm library - Tutorial @@ -25,7 +25,7 @@
      Hello World!
      A stop watch
      Defining states and events
      -
      Adding transitions
      +
      Adding reactions
      State-local storage
      State queries
      A digital camera
      @@ -33,6 +33,15 @@
      Guards, junctions and choice points
      In-state reactions (aka inner transitions)
      Transition actions
      +
      Advanced topics
      +
      Reaction functions
      +
      Reactions
      +
      Specifying multiple reactions for a state
      +
      Posting events
      +
      Deferring events
      +
      Orthogonal states
      +
      Exception handling
      +
      Submachines

      Introduction

      @@ -41,7 +50,7 @@ requires some familiarity with the state machine concept and UML state charts. A href="http://www.objectmentor.com/resources/articles/umlfsm.pdf">here. The UML specifications can be found here (see chapters 2.12 and 3.74).

      Hello World!

      -

      We will follow the tradition and use the simplest possible program to make our first steps. We will implement the following state chart:

      +

      We follow the tradition and use the simplest possible program to make our first steps. We will implement the following state chart:

      #include <boost/fsm/state_machine.hpp>
       #include <boost/fsm/simple_state.hpp>
      @@ -103,7 +112,7 @@ object is destroyed what automatically exits the Greeting state.

      -

      The following is one way to specify that in UML:

      +

      Here is one way to specify this in UML:

      Defining states and events

      The two buttons are modeled by two events. Moreover, we also define the necessary states and the initial state. The following code is @@ -122,8 +131,8 @@ struct StopWatch : public fsm::state_machine< StopWatch, Active > {}; struct Stopped; -struct Active : public fsm::simple_state< - Active, StopWatch, fsm::no_transitions, Stopped > {}; +struct Active : public fsm::simple_state< Active, StopWatch, + fsm::no_reactions, Stopped > {}; struct Running : public fsm::simple_state< Running, Active > {}; struct Stopped : @@ -139,7 +148,7 @@ int main()

      -

      Adding transitions

      -

      With boost::fsm a transition is always defined as a part of the source state. We insert the bold part of the following code:

      +

      Adding reactions

      +

      With boost::fsm a reaction is always defined as part of a state. A reaction +is anything that happens as the result of the processing of an event. For the +moment we will use only one type of reaction: transitions. We insert the bold part of the following code:

      #include <boost/fsm/transition.hpp>
       
       // ...
      @@ -171,8 +182,10 @@ int main()
         myWatch.process_event( EvReset() );
         return 0;
       }
      -

      Of course, an arbitrary number of transitions can originate from a state. That's why we have to put them into an mpl::list<> -as soon as there is more than one of them (see later). Now we have all the states and all the transitions in place and a number of events +

      Of course, a state can define an arbitrary number of reactions. That's why we have to put them into an mpl::list<> +as soon as there is more than one of them (see +later).
      +Now we have all the states and all the transitions in place and a number of events are also sent to the stop watch. The machine will dutifully make the transitions we would expect, but no actions are executed yet.

      State-local storage

      Next we'll make the stop watch actually measure time. Depending on the state the stop watch is in, we need different variables:

      @@ -220,8 +233,7 @@ or indirect outer state object. The same function could be used to access the st The rest should be mostly self-explanatory. The machine now measures the time, but we cannot yet retrieve it from the main program.

      State queries

      Sometimes we need to be able to test whether a machine is in a particular state. That's why the state_machine class template -offers the current_state<>() function. The semantics are very similar to the ones of dynamic_cast (in fact, current_state<>() -is implemented in terms of dynamic_cast). That is, when we call myWatch.current_state< Stopped >() and +offers the current_state<>() function. The semantics are very similar to the ones of dynamic_cast. That is, when we call myWatch.current_state< Stopped >() and the machine is currently in this state, we get a reference to the Stopped state. Otherwise std::bad_cast is thrown. We can use this functionality to implement a StopWatch member function that returns the elapsed time. However, rather than ask the machine in which state it is and then switch to different calculations for the elapsed time, we put the calculation into the @@ -247,7 +259,7 @@ struct StopWatch : // ... -struct Running : public IElapsedTime, fsm::simple_state< +struct Running : public IElapsedTime, public fsm::simple_state< Running, Active, fsm::transition< EvStartStop, Stopped > > { public: @@ -266,13 +278,13 @@ struct Running : public IElapsedTime, fsm::simple_state< std::clock_t startTime_; }; -struct Stopped : public IElapsedTime, fsm::simple_state< +struct Stopped : public IElapsedTime, public fsm::simple_state< Stopped, Active, fsm::transition< EvStartStop, Running > > { - virtual std::clock_t ElapsedTime() const - { - return context< Active >().ElapsedTime(); - } + virtual std::clock_t ElapsedTime() const + { + return context< Active >().ElapsedTime(); + } }; int main() @@ -293,27 +305,32 @@ int main()

      To actually see time being measured, you might want to single-step through the statements in main(). The StopWatch example extends this program to an interactive console application.

      A digital camera

      -

      So far so good. However, the approach used so far has a few limitations:

      +

      So far so good. However, the approach presented above has a few limitations:

      • Bad scalability: As soon as the compiler reaches the point where state_machine::initiate() is called, a number of template instantiations take place which can only succeed if the full declaration of each and every state of the machine is known. That is, the whole layout of a state machine must be implemented in one single translation unit (actions can be compiled separately, but this - is only of minor importance here). For bigger (and more real-world) state machines, this leads to the following limitations: + is of no importance here). For bigger (and more real-world) state machines, this leads to the following limitations:
        • At some point compilers reach their internal template instantiation limits and give up. This can happen even for moderately-sized machines. For example, in debug mode one popular compiler refuses to compile the BitMachine example for anything above 3 bits. This means that the compiler reaches its limits somewhere between 8 states, 24 transitions and 16 states, 64 transitions.
        • -
        • Multiple programmers cannot work on the same state machine seamlessly because every layout change will inevitably lead to a +
        • Multiple programmers can hardly work on the same state machine + simultaneously because every layout change will inevitably lead to a recompilation of the whole state machine.
      • -
      • Maximum one transition per event: According to UML a state can have multiple outgoing transitions triggered by the same event. This - makes sense when all transitions have guards. There is no way to achieve this with fsm::transition. Moreover, the related - UML concepts junction and choice point are not directly supported by this library.
      • -
      • fsm::transition cannot be used for inner transitions (aka in-state reactions).
      • +
      • Maximum one reaction per event: According to UML a state can have multiple + reactions triggered by the same event. This makes sense when all reactions + have mutually exclusive guards. The interface we used above only allows for at + most one unguarded reaction for each event. Moreover, the UML concepts junction and choice point are not directly supported.
      • +
      • There is no way to specify in-state reactions (aka + inner transitions).
      -

      All these problems can be addressed with custom handlers. Warning: It is easy to abuse custom handlers up to the point of invoking -undefined behavior. Please study all the examples and the documentation closely before employing custom handlers!

      +

      All these limitations can be overcome with custom reactions. Warning: It is easy to abuse custom +reactions up to the point of invoking +undefined behavior. Please study the documentation before employing +them!

      Spreading a state machine over multiple translation units

      Let's say your company would like to develop a digital camera. The camera has the following controls:

      -

      Of course, transition actions can also be invoked from custom handlers:

      +

      Of course, transition actions can also be invoked from custom reactions:

      Shooting.cpp:

      // ...
      -bool Focusing::handle_event( const EvInFocus & evt )
      +fsm::result Focusing::react( const EvInFocus & evt )
       {
      -  return transit_to< Focused >( &Shooting::DisplayFocused, evt );
      +  return transit< Focused >( &Shooting::DisplayFocused, evt );
       }
      -

      Please note that we have to manually forward the event because the transition action takes it as its only parameter.

      +

      Please note that we have to manually forward the event.

      +

      Advanced topics

      +

      Reaction functions

      +

      The following functions can only be called from within react overrides. The override must +return by calling exactly one function (e.g. return terminate();):

      + +

      Reactions

      +

      Reactions other than custom_reaction are nothing but syntactic sugar so that users don't have to write +react overrides for common cases. Here's a list of the currently +supplied reactions:

      + +

      Should a user find herself implementing similar react overrides very often, +she can easily define her own reaction and use it just like the ones +that come with boost::fsm.

      +

      Specifying multiple reactions +for a state

      +

      Often a state must define reactions for more than one event. In this case, an +mpl::list must be used as outlined below:

      +
      struct Playing: public fsm::simple_state< Playing, Mp3Player,
      +  mpl::list<
      +    fsm::custom_reaction< EvFastForward >,
      +    fsm::transition< EvStop, Stopped > > > { /* ... */ };
      +

      Posting events

      +

      Non-trivial state machines often need to post internal events. + Here's an example of how to do this with boost::fsm:

      +
      Pumping::~Pumping()
      +{
      +  post_event( boost::intrusive_ptr< EvPumpingFinished >(
      +    new EvPumpingFinished() ) );
      +}
      +

      The event is pushed into the main queue, which is why it must be + allocated with new. The events in the queue are processed as soon as the + current reaction is completed. Events can be posted from inside react + functions, entry-, exit- and transition actions. However, posting from inside + entry actions is a bit more complicated (see e.g. Focusing::Focusing in + Shooting.cpp in the Camera example):

      +
      struct Pumping : public fsm::state< Pumping, Purifier >
      +{
      +  Pumping( my_context ctx ) : my_base( ctx )
      +  {
      +    post_event( boost::intrusive_ptr< EvPumpingStarted >(
      +      new EvPumpingStarted() ) );
      +  }
      +  // ...
      +};
      +

      Please note the bold parts. As soon as an entry action of a state needs + to contact the "outside world" (here: the event queue in the state machine), + the state must derive from fsm::state rather than from fsm::simple_state and + must implement a forwarding constructor as outlined above (apart from the +constructor, fsm::state offers the same interface as fsm::simple_state). The reason for this +admittedly ugly workaround lies in the fact that there's no way to inherit the +base class' constructor in C++, although this would sometimes make perfect +sense.
      +Hence, this must be done + + whenever an entry action makes one or more calls to the following functions:

      + +

      In my experience, these functions are needed only rarely in entry actions +so this workaround should not uglify user code too much.

      +

      Deferring events

      +

      To avoid a number of overheads, event deferral with boost::fsm has one +limitation: Only events allocated with new and pointed to by a + boost::intrusive_ptr can be deferred. Any attempt to defer a +differently allocated event will result in a failing runtime assert. Example:

      +
      class Event : public fsm::event< Event > {};
      +struct Initial;
      +struct Machine : public fsm::state_machine<
      +  Machine, Initial > {};
      +struct Initial : public fsm::simple_state< Initial, Machine,
      +  fsm::deferal< Event > > {};
      +
      +int main()
      +{
      +  Machine myMachine;
      +  myMachine.initiate();
      +  myMachine.process_event( Event() ); // error
      +  myMachine.process_event(
      +    *boost::shared_ptr< Event >( new Event() ) ); // error
      +  myMachine.process_event(
      +    *boost::intrusive_ptr< Event >( new Event() ) ); // fine
      +  return 0;
      +}
      +

      Orthogonal states

      +

      +

      To implement this state chart with boost::fsm, you simply specify more than +one inner initial state:

      +
      struct NumLockOff;
      +struct CapsLockOff;
      +struct ScrollLockOff;
      +struct Active: public fsm::simple_state<
      +  Active, Keyboard, fsm::no_reactions,
      +  mpl::list< NumLockOff, CapsLockOff, ScrollLockOff > > {};
      +

      Actives' inner states must declare which orthogonal region they belong to:

      +
      class EvNumLockPressed : public fsm::event<
      +  EvNumLockPressed > {};
      +class EvCapsLockPressed : public fsm::event<
      +  EvCapsLockPressed > {};
      +class EvScrollLockPressed : public fsm::event<
      +  EvScrollLockPressed > {};
      +
      +struct NumLockOn : public fsm::simple_state<
      +  NumLockOn, Active::orthogonal< 0 >,
      +  fsm::transition< EvNumLockPressed, NumLockOff > > {};
      +struct NumLockOff : public fsm::simple_state<
      +  NumLockOff, Active::orthogonal< 0 >,
      +  fsm::transition< EvNumLockPressed, NumLockOn > > {};
      +
      +struct CapsLockOn : public fsm::simple_state<
      +  CapsLockOn, Active::orthogonal< 1 >,
      +  fsm::transition< EvCapsLockPressed, CapsLockOff > > {};
      +struct CapsLockOff : public fsm::simple_state<
      +  CapsLockOff, Active::orthogonal< 1 >,
      +  fsm::transition< EvCapsLockPressed, CapsLockOn > > {};
      +
      +struct ScrollLockOn : public fsm::simple_state<
      +  ScrollLockOn, Active::orthogonal< 2 >,
      +  fsm::transition< EvScrollLockPressed, ScrollLockOff > > {};
      +struct ScrollLockOff : public fsm::simple_state<
      +  ScrollLockOff, Active::orthogonal< 2 >,
      +  fsm::transition< EvScrollLockPressed, ScrollLockOn > > {};
      +

      orthogonal< 0 > is the default, so NumLockOn and NumLockOff could just as +well pass Active instead of Active::orthogonal< 0 > +to specify their context. The numbers passed to the +orthogonal member template must correspond to the list position in the outer +state. Moreover, the orthogonal position of the source state of a transition +must correspond to the orthogonal position of the target state. Any violations +of these rules lead to compile time errors. Examples:

      +
      // Example 1: does not compile because Active specifies
      +// only 3 orthogonal regions
      +struct WhateverLockOn: public fsm::simple_state<
      +  WhateverLockOn, Active::orthogonal< 3 > > {};
      +
      +// Example 2: does not compile because Active specifies
      +// that NumLockOff is part of the "0th" orthogonal region
      +struct NumLockOff : public fsm::simple_state<
      +  NumLockOff, Active::orthogonal< 1 > > {};
      +
      +// Example 3: does not compile because a transition between
      +// different orthogonal regions is not permitted
      +struct CapsLockOn : public fsm::simple_state<
      +  CapsLockOn, Active::orthogonal< 1 >,
      +  fsm::transition< EvCapsLockPressed, CapsLockOff > > {};
      +struct CapsLockOff : public fsm::simple_state<
      +  CapsLockOff, Active::orthogonal< 2 >,
      +  fsm::transition< EvCapsLockPressed, CapsLockOn > > {};
      +
      +

      Exception handling

      +

      Exceptions can be propagated from all user code except from state exit +actions (mapped to destructors and destructors should virtually never throw in C++). Out +of the box, state_machine does the following:

      +
        +
      1. The exception is caught.
      2. +
      3. In the catch block, an exception_thrown event is allocated on the stack.
      4. +
      5. Also in the catch block, an immediate dispatch of the exception_thrown + event is attempted. That is, possibly remaining events in the queue are + dispatched only after the exception has been handled successfully.
      6. +
      7. If the exception was handled successfully, the state machine returns to + the client normally. If the exception could not be handled successfully, the original exception is rethrown + so that the client of the state machine can handle the exception.
      8. +
      +

      This behavior is implemented in the exception_translator class, +which is the default for the 3rd state_machine template parameter. +It was introduced only because users would want to change this on some platforms to +work around buggy exception handling implementations (see +later).

      +

      Which states can react to an exception_thrown event?

      +

      This depends on where the exception occurred. There are three scenarios:

      +
        +
      1. The react override of a custom reaction propagates an + exception before calling any of the reaction functions. The state that caused the exception + is first tried for a reaction, so the following machine will transit to + Defective after receiving an EvStart event:
        +
        +
        +
      2. +
      3. A state entry action (constructor) propagates an exception. The outer state + of the state that caused the exception is first tried for a reaction, so the + following machine will transit to Defective after trying to enter Stopped:
        +
        +
      4. +
      5. A transition action propagates an exception. The innermost common outer state of the source and the target + state is first tried for a reaction, so the following machine will transit to + Defective after receiving an EvStartStop event:
        +
        +
      6. +
      +

      As with a normal event, the dispatch algorithm will move outward to find a +reaction if the first tried state does not provide one. However, in contrast +to normal events, it will give up once it has unsuccessfully tried an +outermost state, so the following machine will not transit to +Defective after receiving an EvNumLockPressed event:

      +

      +

      +

      +Instead, the machine is terminated and the original exception rethrown.

      +

      Successful exception handling

      +An exception is considered handled successfully, if: +

      The second condition is important for scenarios 2 and 3 in the last section. +In these scenarios, the state machine is in the middle of a transition when the +exception is handled. The machine would be left in an invalid state, should the +reaction simply discard the event without doing anything else.

      +

      The out of the box behavior for unsuccessful exception handling is to rethrow +the original exception. The state machine is terminated before the exception is +propagated to the user.

      +

      Discriminating exceptions

      +

      Because the exception_event object is dispatched from within the catch block, +we can rethrow and catch the exception in a custom reaction:

      +
      struct Defective : public fsm::simple_state<
      +  Defective, Purifier > {};
      +
      +struct Idle : public fsm::simple_state< Idle, Purifier,
      +  mpl::list<
      +    fsm::custom_reaction< EvStart >,
      +    fsm::custom_reaction< exception_thrown > > >
      +{
      +  virtual fsm::result react( const EvStart & )
      +  {
      +    throw std::runtime_error();
      +  }
      +
      +  virtual fsm::result react( const exception_thrown & )
      +  {
      +    try
      +    {
      +      throw;
      +    }
      +    catch ( const std::runtime_error & )
      +    {
      +      // only std::runtime_errors will lead to a transition
      +      // Defective, all other exceptions are propagated to
      +      // the state machine client 
      +      return transit< Defective >();
      +    }
      +  }
      +};
      +

      Unfortunately, this idiom does not work on at least one very popular +compiler. If you have to use one of these platforms, you can pass a +customized exception translator class to the state_machine class template. This +will allow you generate different events depending on the type of the exception.

      +

      Submachines

      +

      Submachines are to event-driven programming what functions are to procedural +programming, reusable building blocks implementing often needed functionality. +The associated UML notation is not entirely clear to me. It seems to be severely +limited (e.g. the same submachine cannot appear in different orthogonal regions) +and does not seem to account for obvious stuff like e.g. parameters.

      +

      boost::fsm is completely unaware of submachines but they can be implemented +quite nicely with templates. Here, a submachine is used to improve the copy-paste implementation of the keyboard machine discussed +above:

      +
      enum LockType
      +{
      +  NUM_LOCK,
      +  CAPS_LOCK,
      +  SCROLL_LOCK
      +};
      +
      +template< LockType lockType >
      +struct Off;
      +struct Active : public fsm::simple_state<
      +  Active, Keyboard, fsm::no_reactions, mpl::list<
      +  Off< NUM_LOCK >, Off< CAPS_LOCK >, Off< SCROLL_LOCK > > > {};
      +
      +template< LockType lockType >
      +class Pressed : public fsm::event< Pressed< lockType > > {};
      +
      +template< LockType lockType >
      +struct On : public fsm::simple_state<
      +  On< lockType >, Active::orthogonal< lockType >,
      +  fsm::transition< Pressed< lockType >, Off< lockType > > > {};
      +
      +template< LockType lockType >
      +struct Off : public fsm::simple_state<
      +  Off< lockType >, Active::orthogonal< lockType >,
      +  fsm::transition< Pressed< lockType >, On< lockType > > > {};
      +

      Of course, the On and Off templates could be given additional parameters to make them truly reusable.


      -

      Revised 07 April, 2003

      -

      © Copyright Andreas Huber 2003. All Rights Reserved.

      +

      Revised +19 May, 2003

      +

      Copyright © 2003 Andreas Huber Dönni. All Rights Reserved.

      - + \ No newline at end of file diff --git a/example/BitMachine/BitMachine.cpp b/example/BitMachine/BitMachine.cpp index 8e21b3e..c3854c0 100644 --- a/example/BitMachine/BitMachine.cpp +++ b/example/BitMachine/BitMachine.cpp @@ -1,5 +1,5 @@ ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied @@ -8,31 +8,8 @@ -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - - -namespace fsm = boost::fsm; -namespace mpl = boost::mpl; - - - ////////////////////////////////////////////////////////////////////////////// -const unsigned int noOfBits = 5; +const unsigned int noOfBits = 1; ////////////////////////////////////////////////////////////////////////////// // This program demonstrates the fact that measures must be taken to hide some // of the complexity (e.g. in separate .cpp file) of a boost::fsm state @@ -47,15 +24,15 @@ const unsigned int noOfBits = 5; // BitState< 15 > and receives EvFlipBit< 4 > it transitions to BitState< 31 > // etc. // The maximum size of such a state machine depends on your compiler. The -// following table gives upper limits for noOfBits. From this, *rough* +// following table gives upper limits for noOfBits. From this, rough // estimates for the maximum size of any "naively" implemented boost::fsm // (i.e. no attempt is made to hide inner state implementation in a .cpp file) // can be deduced. // -// Compiler | max. noOfBits | max. states | max. transitions -// --------------|---------------|-------------|----------------- -// MSVC 7.0 | < 5 | < 32 | < 120 -// MSVC 7.1 | < 6 | < 64 | < 384 +// Compiler | max. noOfBits b | max. states s | max. transitions t +// --------------|-----------------|---------------|------------------- +// MSVC 7.0 | b < 5 | 16 < s < 32 | 64 < t < 120 +// MSVC 7.1 | b < 6 | 32 < s < 64 | 120 < t < 384 // // CAUTION: Due to the fact that the amount of generated code more than // *doubles* each time noOfBits is *incremented*, build times soar when @@ -64,6 +41,62 @@ const unsigned int noOfBits = 5; +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef BOOST_MSVC +#pragma warning( push ) +#pragma warning( disable: 4800 ) // forcing value to bool 'true' or 'false' +#pragma warning( disable: 4127 ) // conditional expression is constant +#endif + +#define BOOST_NO_MT +#include + +#ifdef BOOST_MSVC +#pragma warning( pop ) +#endif + +#include +#include +#include + +#include "UniqueObject.hpp" + + +namespace fsm = boost::fsm; +namespace mpl = boost::mpl; +using namespace mpl::placeholders; + + + +const unsigned int noOfStates = 1 << noOfBits; +const unsigned int noOfTransitions = noOfStates * noOfBits; + +const unsigned int noOfEvents = 3 * 3 * 5 * 7 * 31 * 127; +const unsigned int noOfLaps = noOfEvents / ( noOfStates - 1 ); + +unsigned long eventsSentTotal = 0; + + + +////////////////////////////////////////////////////////////////////////////// void DisplayBits( unsigned int number ) { char buffer[ noOfBits + 1 ]; @@ -79,21 +112,36 @@ void DisplayBits( unsigned int number ) } +////////////////////////////////////////////////////////////////////////////// template< unsigned int bitNo > class EvFlipBit : public fsm::event< EvFlipBit > {}; +const EvFlipBit< 0 > flip0; +const EvFlipBit< 1 > flip1; +const EvFlipBit< 2 > flip2; +const EvFlipBit< 3 > flip3; +const EvFlipBit< 4 > flip4; +const EvFlipBit< 5 > flip5; +const EvFlipBit< 6 > flip6; +const EvFlipBit< 7 > flip7; +const EvFlipBit< 8 > flip8; +const EvFlipBit< 9 > flip9; +const fsm::event_base * const pFlipBitEvents[] = + { &flip0, &flip1, &flip2, &flip3, &flip4, &flip5, &flip6, &flip7, &flip8, &flip9 }; + template< unsigned int stateNo > class BitState; -class BitMachine : public fsm::state_machine< BitMachine, BitState< 0 > > {}; - -using namespace mpl::placeholders; - +////////////////////////////////////////////////////////////////////////////// +class BitMachine : public fsm::state_machine< BitMachine, BitState< 0 >, + fsm::exception_translator, boost::fast_pool_allocator< int > > {}; +////////////////////////////////////////////////////////////////////////////// template< class BitNo, class StateNo > struct FlipTransition { - BOOST_STATIC_CONSTANT( unsigned int, nextStateNo=StateNo::value ^ ( 1 << BitNo::value ) ); + BOOST_STATIC_CONSTANT( + unsigned int, nextStateNo=StateNo::value ^ ( 1 << BitNo::value ) ); BOOST_STATIC_CONSTANT( unsigned int, bitNo=BitNo::value ); typedef fsm::transition< @@ -103,45 +151,84 @@ struct FlipTransition BOOST_MPL_AUX_LAMBDA_SUPPORT( 2, FlipTransition, (BitNo, StateNo) ) }; +////////////////////////////////////////////////////////////////////////////// template< unsigned int stateNo > struct FlipTransitionList { private: + ////////////////////////////////////////////////////////////////////////// typedef mpl::fold< mpl::range_c< unsigned int, 0, noOfBits >, mpl::list<>, mpl::push_front< _, _ > >::type BitNumbers; + public: - typedef typename mpl::transform< - BitNumbers, + ////////////////////////////////////////////////////////////////////////// + typedef typename mpl::transform< BitNumbers, FlipTransition< _, mpl::integral_c< unsigned int, stateNo > > >::type type; }; -template< unsigned int stateNo > -class BitState : public fsm::simple_state< - BitState, BitMachine, typename FlipTransitionList< stateNo >::type > + +////////////////////////////////////////////////////////////////////////////// +class IDisplay { public: - BitState() + ////////////////////////////////////////////////////////////////////////// + virtual void DisplayBits() const = 0; +}; + + +////////////////////////////////////////////////////////////////////////////// +template< unsigned int stateNo > +class BitState : public fsm::simple_state< + BitState< stateNo >, BitMachine, typename FlipTransitionList< stateNo >::type >, + public IDisplay, public UniqueObject< BitState< stateNo > > +{ + public: + ////////////////////////////////////////////////////////////////////////// + virtual void DisplayBits() const { - DisplayBits( stateNo ); + ::DisplayBits( stateNo ); } }; - -template< unsigned int msb > +////////////////////////////////////////////////////////////////////////////// +template< unsigned int msb, bool display > void VisitAllStates( BitMachine & bitMachine ) { - VisitAllStates< msb - 1 >( bitMachine ); - bitMachine.process_event( EvFlipBit< msb >() ); - VisitAllStates< msb - 1 >( bitMachine ); + VisitAllStates< msb - 1, display >( bitMachine ); + bitMachine.process_event( *pFlipBitEvents[ msb ] ); + ++eventsSentTotal; + +#ifdef BOOST_MSVC +#pragma warning( push ) +#pragma warning( disable: 4127 ) +#endif + if ( display ) + { + bitMachine.current_state< IDisplay >().DisplayBits(); + } +#ifdef BOOST_MSVC +#pragma warning( pop ) +#endif + + VisitAllStates< msb - 1, display >( bitMachine ); } template<> -void VisitAllStates< 0 >( BitMachine & bitMachine ) +void VisitAllStates< 0, false >( BitMachine & bitMachine ) { - bitMachine.process_event( EvFlipBit< 0 >() ); + bitMachine.process_event( *pFlipBitEvents[ 0 ] ); + ++eventsSentTotal; +} + +template<> +void VisitAllStates< 0, true >( BitMachine & bitMachine ) +{ + bitMachine.process_event( *pFlipBitEvents[ 0 ] ); + ++eventsSentTotal; + bitMachine.current_state< IDisplay >().DisplayBits(); } @@ -153,18 +240,22 @@ char GetKey() } +////////////////////////////////////////////////////////////////////////////// int main( int argc, char * argv[] ) { argc; argv; - std::cout << "boost::fsm BitMachine example\n\n"; + std::cout << "boost::fsm BitMachine example\n"; + std::cout << "Machine configuration: " << noOfStates << + " states interconnected with " << noOfTransitions << " transitions.\n\n"; for ( unsigned int bit = 0; bit < noOfBits; ++bit ) { std::cout << bit - 0 << ": Flips bit " << bit - 0 << "\n"; } - std::cout << "a: Goes through all states automatically\n\n"; + std::cout << "a: Goes through all states automatically\n"; + std::cout << "p: Performance test\n"; std::cout << "e: Exits the program\n\n"; std::cout << "You may chain commands, e.g. 31 flips bits 3 and 1\n\n"; @@ -176,80 +267,47 @@ int main( int argc, char * argv[] ) while ( key != 'e' ) { - if ( ( key >= '0' ) && ( key <= '0' + noOfBits - 1 ) || ( key == 'a' ) ) + if ( ( key >= '0' ) && ( key < '0' + noOfBits ) ) { - switch( key ) - { - case '0': - { - bitMachine.process_event( EvFlipBit< 0 >() ); - } - break; - - case '1': - { - bitMachine.process_event( EvFlipBit< 1 >() ); - } - break; - - case '2': - { - bitMachine.process_event( EvFlipBit< 2 >() ); - } - break; - - case '3': - { - bitMachine.process_event( EvFlipBit< 3 >() ); - } - break; - - case '4': - { - bitMachine.process_event( EvFlipBit< 4 >() ); - } - break; - - case '5': - { - bitMachine.process_event( EvFlipBit< 5 >() ); - } - break; - - case '6': - { - bitMachine.process_event( EvFlipBit< 6 >() ); - } - break; - - case '7': - { - bitMachine.process_event( EvFlipBit< 7 >() ); - } - break; - - case '8': - { - bitMachine.process_event( EvFlipBit< 8 >() ); - } - break; - - case '9': - { - bitMachine.process_event( EvFlipBit< 9 >() ); - } - break; - - case 'a': - { - VisitAllStates< noOfBits - 1 >( bitMachine ); - } - break; - } + bitMachine.process_event( *pFlipBitEvents[ key - '0' ] ); + ++eventsSentTotal; + bitMachine.current_state< IDisplay >().DisplayBits(); } else { - std::cout << "Invalid key!\n"; + switch( key ) + { + case 'a': + { + VisitAllStates< noOfBits - 1, true >( bitMachine ); + } + break; + + case 'p': + { + std::cout << "\nSending " << noOfEvents << " events. Please wait...\n"; + + const unsigned long startEvents2 = eventsSentTotal; + const std::clock_t startTime2 = std::clock(); + + for ( unsigned int lap = 0; lap < noOfLaps; ++lap ) + { + VisitAllStates< noOfBits - 1, false >( bitMachine ); + } + + const std::clock_t elapsedTime2 = std::clock() - startTime2; + const unsigned int eventsSent2 = eventsSentTotal - startEvents2; + std::cout << "Time to dispatch one event and\n" << + "perform the resulting transition: "; + std::cout << elapsedTime2 * 1000.0 / eventsSent2 << " microseconds\n\n"; + } + break; + + default: + { + std::cout << "Invalid key!\n"; + } + } } key = GetKey(); diff --git a/example/BitMachine/BitMachine.vcproj b/example/BitMachine/BitMachine.vcproj index b335f57..10ad99c 100644 --- a/example/BitMachine/BitMachine.vcproj +++ b/example/BitMachine/BitMachine.vcproj @@ -26,9 +26,9 @@ MinimalRebuild="TRUE" BasicRuntimeChecks="3" SmallerTypeCheck="TRUE" - RuntimeLibrary="1" + RuntimeLibrary="5" BufferSecurityCheck="TRUE" - DisableLanguageExtensions="TRUE" + DisableLanguageExtensions="FALSE" TreatWChar_tAsBuiltInType="TRUE" ForceConformanceInForLoopScope="TRUE" RuntimeTypeInfo="TRUE" @@ -80,16 +80,16 @@ Optimization="3" GlobalOptimizations="TRUE" InlineFunctionExpansion="2" - FavorSizeOrSpeed="2" + FavorSizeOrSpeed="1" OmitFramePointers="TRUE" - OptimizeForProcessor="2" + OptimizeForProcessor="0" AdditionalIncludeDirectories="..\..\..\..\..\boost_1_30_0;"..\..\..\..\..\boost-sandbox"" PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" StringPooling="TRUE" SmallerTypeCheck="FALSE" - RuntimeLibrary="0" + RuntimeLibrary="4" EnableFunctionLevelLinking="TRUE" - DisableLanguageExtensions="TRUE" + DisableLanguageExtensions="FALSE" TreatWChar_tAsBuiltInType="TRUE" ForceConformanceInForLoopScope="TRUE" RuntimeTypeInfo="TRUE" @@ -145,13 +145,19 @@ Name="fsm Header Files" Filter="h;hpp;hxx;hm;inl;inc"> + RelativePath="..\..\..\..\boost\fsm\custom_reaction.hpp"> + RelativePath="..\..\..\..\boost\fsm\event_base.hpp"> + + + + @@ -163,12 +169,22 @@ RelativePath="..\..\..\..\boost\fsm\state_machine.hpp"> + RelativePath="..\..\..\..\boost\fsm\termination.hpp"> + + + + + + diff --git a/example/BitMachine/UniqueObject.hpp b/example/BitMachine/UniqueObject.hpp new file mode 100644 index 0000000..6cdaedf --- /dev/null +++ b/example/BitMachine/UniqueObject.hpp @@ -0,0 +1,41 @@ +#ifndef UNIQUE_OBJECT_HPP_INCLUDED +#define UNIQUE_OBJECT_HPP_INCLUDED +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +////////////////////////////////////////////////////////////////////////////// + + + +#include "UniqueObjectAllocator.hpp" + + + +////////////////////////////////////////////////////////////////////////////// +template< class Derived > +class UniqueObject +{ + public: + ////////////////////////////////////////////////////////////////////////// + static void * operator new( size_t size ) + { + return UniqueObjectAllocator< Derived >::allocate( size ); + } + + static void operator delete( void * p, size_t size ) + { + UniqueObjectAllocator< Derived >::deallocate( p, size ); + } + + protected: + ////////////////////////////////////////////////////////////////////////// + UniqueObject() {} + ~UniqueObject() {} +}; + + + +#endif diff --git a/example/BitMachine/UniqueObjectAllocator.hpp b/example/BitMachine/UniqueObjectAllocator.hpp new file mode 100644 index 0000000..8c57d8e --- /dev/null +++ b/example/BitMachine/UniqueObjectAllocator.hpp @@ -0,0 +1,60 @@ +#ifndef UNIQUE_OBJECT_ALLOCATOR_HPP_INCLUDED +#define UNIQUE_OBJECT_ALLOCATOR_HPP_INCLUDED +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +////////////////////////////////////////////////////////////////////////////// + + + +#include // size_t + + + +////////////////////////////////////////////////////////////////////////////// +template< class T > +class UniqueObjectAllocator +{ + public: + ////////////////////////////////////////////////////////////////////////// + static void * allocate( size_t size ) + { + size; + BOOST_ASSERT( !constructed_ && ( size == sizeof( T ) ) ); + constructed_ = true; + return &storage_.data[ 0 ]; + } + + static void deallocate( void * p, size_t size ) + { + p; + size; + BOOST_ASSERT( constructed_ && + ( p == &storage_.data[ 0 ] ) && ( size == sizeof( T ) ) ); + constructed_ = false; + } + + private: + ////////////////////////////////////////////////////////////////////////// + union storage + { + char data[ sizeof( T ) ]; + typename boost::type_with_alignment< + boost::alignment_of< T >::value >::type aligner_; + }; + + static bool constructed_; + static storage storage_; +}; + +template< class T > +bool UniqueObjectAllocator< T >::constructed_ = false; +template< class T > +typename UniqueObjectAllocator< T >::storage UniqueObjectAllocator< T >::storage_; + + + +#endif diff --git a/example/Camera/Camera.cpp b/example/Camera/Camera.cpp index f71a251..3c2229f 100644 --- a/example/Camera/Camera.cpp +++ b/example/Camera/Camera.cpp @@ -1,5 +1,5 @@ ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied @@ -25,15 +25,15 @@ NotShooting::~NotShooting() std::cout << "Exiting NotShooting\n"; } -bool NotShooting::handle_event( const EvShutterHalf & ) +fsm::result NotShooting::react( const EvShutterHalf & ) { if ( context< Camera >().IsBatteryLow() ) { - return false; + return forward_event(); } else { - return transit_to< Shooting >(); + return transit< Shooting >(); } } @@ -48,7 +48,7 @@ Idle::~Idle() std::cout << "Exiting Idle\n"; } -bool Idle::handle_event( const EvConfig & ) +fsm::result Idle::react( const EvConfig & ) { - return transit_to< Configuring >(); + return transit< Configuring >(); } diff --git a/example/Camera/Camera.hpp b/example/Camera/Camera.hpp index 0774a5d..a927131 100644 --- a/example/Camera/Camera.hpp +++ b/example/Camera/Camera.hpp @@ -1,7 +1,7 @@ #ifndef CAMERA_HPP #define CAMERA_HPP ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied @@ -13,7 +13,7 @@ #include #include #include -#include +#include namespace fsm = boost::fsm; @@ -33,28 +33,23 @@ struct Camera : public fsm::state_machine< Camera, NotShooting > }; -// With fsm::transition the target state must be a complete type. That is, -// Configuring.hpp and Shooting.hpp would have to be included in this header. -// Instead, a custom handler is used. This allows us to make the transitions -// in the respective .cpp files and to loosen the coupling to the target -// states. struct Idle; struct NotShooting : public fsm::simple_state< NotShooting, Camera, - fsm::custom_handler< EvShutterHalf >, Idle > + fsm::custom_reaction< EvShutterHalf >, Idle > { NotShooting(); ~NotShooting(); - virtual bool handle_event( const EvShutterHalf & ); + virtual fsm::result react( const EvShutterHalf & ); }; struct Idle : public fsm::simple_state< Idle, NotShooting, - fsm::custom_handler< EvConfig > > + fsm::custom_reaction< EvConfig > > { Idle(); ~Idle(); - virtual bool handle_event( const EvConfig & ); + virtual fsm::result react( const EvConfig & ); }; diff --git a/example/Camera/Camera.vcproj b/example/Camera/Camera.vcproj index 9e31d54..0dc562f 100644 --- a/example/Camera/Camera.vcproj +++ b/example/Camera/Camera.vcproj @@ -27,7 +27,7 @@ SmallerTypeCheck="TRUE" RuntimeLibrary="1" BufferSecurityCheck="TRUE" - DisableLanguageExtensions="TRUE" + DisableLanguageExtensions="FALSE" TreatWChar_tAsBuiltInType="TRUE" ForceConformanceInForLoopScope="TRUE" RuntimeTypeInfo="TRUE" @@ -36,7 +36,8 @@ WarningLevel="4" WarnAsError="TRUE" Detect64BitPortabilityProblems="TRUE" - DebugInformationFormat="4"/> + DebugInformationFormat="4" + ShowIncludes="FALSE"/> + DebugInformationFormat="0" + ShowIncludes="FALSE"/> + RelativePath="..\..\..\..\boost\fsm\custom_reaction.hpp"> + + + + @@ -203,7 +211,7 @@ RelativePath="..\..\..\..\boost\fsm\state_machine.hpp"> + RelativePath="..\..\..\..\boost\fsm\termination.hpp"> diff --git a/example/Camera/Configuring.cpp b/example/Camera/Configuring.cpp index 51d5ccc..6bb62ab 100644 --- a/example/Camera/Configuring.cpp +++ b/example/Camera/Configuring.cpp @@ -1,5 +1,5 @@ ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied diff --git a/example/Camera/Configuring.hpp b/example/Camera/Configuring.hpp index 85b955a..702e3dd 100644 --- a/example/Camera/Configuring.hpp +++ b/example/Camera/Configuring.hpp @@ -1,7 +1,7 @@ #ifndef CONFIGURING_HPP #define CONFIGURING_HPP ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied diff --git a/example/Camera/Main.cpp b/example/Camera/Main.cpp index 8a95f1a..3b5dc20 100644 --- a/example/Camera/Main.cpp +++ b/example/Camera/Main.cpp @@ -1,5 +1,5 @@ ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied @@ -10,7 +10,7 @@ ////////////////////////////////////////////////////////////////////////////// // This program shows how a state machine can be spread over several -// translation units if necessary. A inner workings of a digital camera are +// translation units if necessary. The inner workings of a digital camera are // modeled, the corresponding state chart looks as follows: // // --------------------------- diff --git a/example/Camera/Precompiled.cpp b/example/Camera/Precompiled.cpp index be2fecc..f06af24 100644 --- a/example/Camera/Precompiled.cpp +++ b/example/Camera/Precompiled.cpp @@ -1,8 +1,11 @@ ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied // warranty, and with no claim as to its suitability for any purpose. ////////////////////////////////////////////////////////////////////////////// + + + #include "Precompiled.hpp" diff --git a/example/Camera/Precompiled.hpp b/example/Camera/Precompiled.hpp index d9469cb..296f9d8 100644 --- a/example/Camera/Precompiled.hpp +++ b/example/Camera/Precompiled.hpp @@ -1,7 +1,7 @@ #ifndef PRECOMPILED_HPP #define PRECOMPILED_HPP ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include diff --git a/example/Camera/Shooting.cpp b/example/Camera/Shooting.cpp index aad7921..31b1274 100644 --- a/example/Camera/Shooting.cpp +++ b/example/Camera/Shooting.cpp @@ -1,5 +1,5 @@ ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied @@ -35,23 +35,21 @@ struct Storing : public fsm::simple_state< Storing, Shooting > struct Focused : public fsm::simple_state< Focused, Shooting, - fsm::custom_handler< EvShutterFull > > + fsm::custom_reaction< EvShutterFull > > { - virtual bool handle_event( const EvShutterFull & ); + virtual fsm::result react( const EvShutterFull & ); }; -bool Focused::handle_event( const EvShutterFull & ) +fsm::result Focused::react( const EvShutterFull & ) { if ( context< Camera >().IsMemoryAvailable() ) { - return transit_to< Storing >(); + return transit< Storing >(); } else { std::cout << "Cache memory full. Please wait...\n"; - // Indicate that we have consumed the event. So, the - // dispatch algorithm will stop looking for a handler. - return true; + return discard_event(); } } @@ -60,10 +58,10 @@ struct EvInFocus : public fsm::event< EvInFocus > {}; Focusing::Focusing( my_context ctx ) : my_base( ctx ) { - post_event( boost::shared_ptr< EvInFocus >( new EvInFocus() ) ); + post_event( boost::intrusive_ptr< EvInFocus >( new EvInFocus() ) ); } -bool Focusing::handle_event( const EvInFocus & evt ) +fsm::result Focusing::react( const EvInFocus & evt ) { - return transit_to< Focused >( &Shooting::DisplayFocused, evt ); + return transit< Focused >( &Shooting::DisplayFocused, evt ); } diff --git a/example/Camera/Shooting.hpp b/example/Camera/Shooting.hpp index 4143cb4..b5644da 100644 --- a/example/Camera/Shooting.hpp +++ b/example/Camera/Shooting.hpp @@ -1,7 +1,7 @@ #ifndef SHOOTING_HPP #define SHOOTING_HPP ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied @@ -15,11 +15,10 @@ #include #include #include -#include +#include #include namespace fsm = boost::fsm; -namespace mpl = boost::mpl; @@ -38,10 +37,10 @@ struct Shooting : public fsm::simple_state< Shooting, Camera, }; struct Focusing : public fsm::state< Focusing, Shooting, - fsm::custom_handler< EvInFocus > > + fsm::custom_reaction< EvInFocus > > { Focusing( my_context ctx ); - virtual bool handle_event( const EvInFocus & ); + virtual fsm::result react( const EvInFocus & ); }; diff --git a/example/StateSize/StateSize.cpp b/example/StateSize/StateSize.cpp index 3e0ec64..1e007e3 100644 --- a/example/StateSize/StateSize.cpp +++ b/example/StateSize/StateSize.cpp @@ -1,5 +1,5 @@ ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include @@ -38,40 +38,40 @@ class DummyMachine : public fsm::state_machine< DummyMachine, UnconnectedOuterSt class UnconnectedInnerState; -class UnconnectedOuterState : public fsm::simple_state< UnconnectedOuterState, DummyMachine, fsm::no_transitions, mpl::list< UnconnectedInnerState > > {}; +class UnconnectedOuterState : public fsm::simple_state< UnconnectedOuterState, DummyMachine, fsm::no_reactions, mpl::list< UnconnectedInnerState > > {}; class UnconnectedInnerState : public fsm::simple_state< UnconnectedInnerState, UnconnectedOuterState > {}; class OneTransitionInnerState; class OneTransitionOuterState : public fsm::simple_state< OneTransitionOuterState, DummyMachine, - fsm::terminator< DummyEvent1 >, OneTransitionInnerState > {}; + fsm::termination< DummyEvent1 >, OneTransitionInnerState > {}; class OneTransitionInnerState : public fsm::simple_state< OneTransitionInnerState, OneTransitionOuterState, - fsm::terminator< DummyEvent1 > > {}; + fsm::termination< DummyEvent1 > > {}; class TwoTransitionInnerState; class TwoTransitionOuterState : public fsm::simple_state< TwoTransitionOuterState, DummyMachine, mpl::list< - fsm::terminator< DummyEvent1 >, - fsm::terminator< DummyEvent2 > >, + fsm::termination< DummyEvent1 >, + fsm::termination< DummyEvent2 > >, TwoTransitionInnerState > {}; class TwoTransitionInnerState : public fsm::simple_state< TwoTransitionInnerState, TwoTransitionOuterState, mpl::list< - fsm::terminator< DummyEvent1 >, - fsm::terminator< DummyEvent2 > > > {}; + fsm::termination< DummyEvent1 >, + fsm::termination< DummyEvent2 > > > {}; class ThreeTransitionInnerState; class ThreeTransitionOuterState : public fsm::simple_state< ThreeTransitionOuterState, DummyMachine, mpl::list< - fsm::terminator< DummyEvent1 >, - fsm::terminator< DummyEvent2 >, - fsm::terminator< DummyEvent3 > >, + fsm::termination< DummyEvent1 >, + fsm::termination< DummyEvent2 >, + fsm::termination< DummyEvent3 > >, ThreeTransitionInnerState > {}; class ThreeTransitionInnerState : public fsm::simple_state< ThreeTransitionInnerState, ThreeTransitionOuterState, mpl::list< - fsm::terminator< DummyEvent1 >, - fsm::terminator< DummyEvent2 >, - fsm::terminator< DummyEvent3 > > > {}; + fsm::termination< DummyEvent1 >, + fsm::termination< DummyEvent2 >, + fsm::termination< DummyEvent3 > > > {}; int main( int argc, char * argv[] ) @@ -81,23 +81,25 @@ int main( int argc, char * argv[] ) std::cout << std::left << "boost::fsm::state sizes\n\n" << + std::setw( 50 ) << "detail::counted_base< unsigned char, false >: " << + sizeof( fsm::detail::counted_base< unsigned char, false > ) << "\n" << std::setw( 50 ) << "detail::state_base: " << sizeof( fsm::detail::state_base ) << "\n" << std::setw( 50 ) << "detail::universal_state< std::list< _ > >: " << sizeof( fsm::detail::universal_state< - std::list< boost::shared_ptr< fsm::detail::state_base > > > ) << "\n" << + std::list< boost::intrusive_ptr< fsm::detail::state_base > > > ) << "\n" << std::setw( 50 ) << "detail::leaf_state< std::list< _ > >: " << sizeof( fsm::detail::leaf_state< - std::list< boost::shared_ptr< fsm::detail::state_base > > > ) << "\n" << + std::list< boost::intrusive_ptr< fsm::detail::state_base > > > ) << "\n" << std::setw( 50 ) << "detail::node_state< 1, std::list< _ > >: " << sizeof( fsm::detail::node_state< 1, - std::list< boost::shared_ptr< fsm::detail::state_base > > > ) << "\n" << + std::list< boost::intrusive_ptr< fsm::detail::state_base > > > ) << "\n" << std::setw( 50 ) << "detail::node_state< 2, std::list< _ > >: " << sizeof( fsm::detail::node_state< 2, - std::list< boost::shared_ptr< fsm::detail::state_base > > > ) << "\n" << + std::list< boost::intrusive_ptr< fsm::detail::state_base > > > ) << "\n" << std::setw( 50 ) << "detail::node_state< 3, std::list< _ > >: " << sizeof( fsm::detail::node_state< 3, - std::list< boost::shared_ptr< fsm::detail::state_base > > > ) << "\n\n" << + std::list< boost::intrusive_ptr< fsm::detail::state_base > > > ) << "\n\n" << std::setw( 50 ) << "simple_state< _, _, no_transitions, _ >: " << sizeof( UnconnectedOuterState ) << "\n" << diff --git a/example/StateSize/StateSize.vcproj b/example/StateSize/StateSize.vcproj index 53bf2a0..221ba27 100644 --- a/example/StateSize/StateSize.vcproj +++ b/example/StateSize/StateSize.vcproj @@ -27,7 +27,7 @@ SmallerTypeCheck="TRUE" RuntimeLibrary="1" BufferSecurityCheck="TRUE" - DisableLanguageExtensions="TRUE" + DisableLanguageExtensions="FALSE" TreatWChar_tAsBuiltInType="TRUE" ForceConformanceInForLoopScope="TRUE" RuntimeTypeInfo="TRUE" @@ -87,7 +87,7 @@ SmallerTypeCheck="FALSE" RuntimeLibrary="0" EnableFunctionLevelLinking="TRUE" - DisableLanguageExtensions="TRUE" + DisableLanguageExtensions="FALSE" TreatWChar_tAsBuiltInType="TRUE" ForceConformanceInForLoopScope="TRUE" RuntimeTypeInfo="TRUE" @@ -143,11 +143,17 @@ Name="fsm Header Files" Filter="h;hpp;hxx;hm;inl;inc"> + RelativePath="..\..\..\..\boost\fsm\custom_reaction.hpp"> + + + + @@ -161,7 +167,7 @@ RelativePath="..\..\..\..\boost\fsm\state_machine.hpp"> + RelativePath="..\..\..\..\boost\fsm\termination.hpp"> diff --git a/example/StopWatch/StopWatch.cpp b/example/StopWatch/StopWatch.cpp index d07e8fc..446738e 100644 --- a/example/StopWatch/StopWatch.cpp +++ b/example/StopWatch/StopWatch.cpp @@ -1,5 +1,5 @@ ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied @@ -8,18 +8,6 @@ -#include -#include -#include -#include - -#include -#include - - -namespace fsm = boost::fsm; - - ////////////////////////////////////////////////////////////////////////////// // The following code implements the state-machine: // -------------------------------- @@ -40,22 +28,41 @@ namespace fsm = boost::fsm; // | ---------------------------- | // -------------------------------- + + +#include +#include +#include +#include + +#include + + +#include +#include + + + +namespace fsm = boost::fsm; +namespace mpl = boost::mpl; + + + class EvStartStop : public fsm::event< EvStartStop > {}; class EvReset : public fsm::event< EvReset > {}; -class IElapsedTime +struct IElapsedTime { - public: - virtual std::clock_t ElapsedTime() const = 0; + virtual std::clock_t ElapsedTime() const = 0; }; -class Active; -class StopWatch : public fsm::state_machine< StopWatch, Active > {}; +struct Active; +struct StopWatch : public fsm::state_machine< StopWatch, Active > {}; -class Stopped; -class Active : +struct Stopped; +struct Active : public fsm::simple_state< Active, StopWatch, fsm::transition< EvReset, Active >, Stopped > { @@ -76,7 +83,7 @@ class Active : std::clock_t elapsedTime_; }; -class Running : +struct Running : public IElapsedTime, public fsm::simple_state< Running, Active, fsm::transition< EvStartStop, Stopped > > @@ -98,16 +105,15 @@ class Running : std::clock_t startTime_; }; -class Stopped : +struct Stopped : public IElapsedTime, public fsm::simple_state< Stopped, Active, fsm::transition< EvStartStop, Running > > { - private: - virtual std::clock_t ElapsedTime() const - { - return context< Active >().ElapsedTime(); - } + virtual std::clock_t ElapsedTime() const + { + return context< Active >().ElapsedTime(); + } }; @@ -169,5 +175,5 @@ int main( int argc, char * argv[] ) key = GetKey(); } - return 0; -} \ No newline at end of file + return 0; +} diff --git a/example/StopWatch/StopWatch.vcproj b/example/StopWatch/StopWatch.vcproj index 85c8d27..6820114 100644 --- a/example/StopWatch/StopWatch.vcproj +++ b/example/StopWatch/StopWatch.vcproj @@ -27,7 +27,7 @@ SmallerTypeCheck="TRUE" RuntimeLibrary="1" BufferSecurityCheck="TRUE" - DisableLanguageExtensions="TRUE" + DisableLanguageExtensions="FALSE" TreatWChar_tAsBuiltInType="TRUE" ForceConformanceInForLoopScope="TRUE" RuntimeTypeInfo="TRUE" @@ -87,7 +87,7 @@ SmallerTypeCheck="FALSE" RuntimeLibrary="0" EnableFunctionLevelLinking="TRUE" - DisableLanguageExtensions="TRUE" + DisableLanguageExtensions="FALSE" TreatWChar_tAsBuiltInType="TRUE" ForceConformanceInForLoopScope="TRUE" RuntimeTypeInfo="TRUE" @@ -143,13 +143,19 @@ Name="fsm Header Files" Filter="h;hpp;hxx;hm;inl;inc"> + RelativePath="..\..\..\..\boost\fsm\custom_reaction.hpp"> + RelativePath="..\..\..\..\boost\fsm\event_base.hpp"> + + + + @@ -161,7 +167,7 @@ RelativePath="..\..\..\..\boost\fsm\state_machine.hpp"> + RelativePath="..\..\..\..\boost\fsm\termination.hpp"> diff --git a/include/boost/statechart/custom_handler.hpp b/include/boost/statechart/custom_reaction.hpp similarity index 63% rename from include/boost/statechart/custom_handler.hpp rename to include/boost/statechart/custom_reaction.hpp index ec59985..a64d863 100644 --- a/include/boost/statechart/custom_handler.hpp +++ b/include/boost/statechart/custom_reaction.hpp @@ -1,7 +1,7 @@ -#ifndef BOOST_FSM_CUSTOM_HANDLER_HPP_INCLUDED -#define BOOST_FSM_CUSTOM_HANDLER_HPP_INCLUDED +#ifndef BOOST_FSM_CUSTOM_REACTION_HPP_INCLUDED +#define BOOST_FSM_CUSTOM_REACTION_HPP_INCLUDED ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied @@ -10,7 +10,7 @@ -#include +#include @@ -22,13 +22,13 @@ namespace fsm template< class Event > -struct custom_handler +struct custom_reaction { - template< class Derived > - struct apply - { - typedef detail::event_handler< Event > type; - }; + template< class Derived > + struct apply + { + typedef detail::reaction< Event > type; + }; }; diff --git a/include/boost/statechart/detail/counted_base.hpp b/include/boost/statechart/detail/counted_base.hpp index fec92d0..4f03e84 100644 --- a/include/boost/statechart/detail/counted_base.hpp +++ b/include/boost/statechart/detail/counted_base.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_FSM_COUNTED_BASE_HPP_INCLUDED #define BOOST_FSM_COUNTED_BASE_HPP_INCLUDED ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied @@ -10,8 +10,11 @@ -#include -#include +#include +#include // BOOST_ASSERT +#include // BOOST_STATIC_CONSTANT + +#include // std::numeric_limits @@ -23,14 +26,26 @@ namespace detail { + +template< bool NeedsLocking > +struct locked_base +{ + typedef ::boost::detail::lightweight_mutex::scoped_lock scoped_lock; + mutable ::boost::detail::lightweight_mutex mutex_; +}; -typedef unsigned char orthogonal_position_type; - - +template<> +struct locked_base< false > +{ + typedef bool scoped_lock; + BOOST_STATIC_CONSTANT( bool, mutex_ = false ); +}; ////////////////////////////////////////////////////////////////////////////// -class counted_base +template< typename CountType, bool NeedsLocking = true > +class counted_base : private locked_base< NeedsLocking > { + typedef locked_base< NeedsLocking > base_type; public: ////////////////////////////////////////////////////////////////////////// virtual ~counted_base() {} @@ -39,9 +54,44 @@ class counted_base ////////////////////////////////////////////////////////////////////////// counted_base() : count_( 0 ) {} + counted_base( const counted_base & ) : count_( 0 ) {} + counted_base & operator=( const counted_base & ) {} + public: ////////////////////////////////////////////////////////////////////////// - mutable orthogonal_position_type count_; + // CAUTION: The following declarations should be private. + // They are only public because many compilers lack template friends. + ////////////////////////////////////////////////////////////////////////// + void add_ref() const + { + base_type::scoped_lock lock( base_type::mutex_ ); + lock; + BOOST_ASSERT( count_ < std::numeric_limits< CountType >::max() ); + ++count_; + } + + void release() const + { + bool shouldDelete = false; + + { + // release the mutex in the base class before the base class object + // is destroyed + base_type::scoped_lock lock( base_type::mutex_ ); + lock; + BOOST_ASSERT( count_ > 0 ); + shouldDelete = ( --count_ == 0 ); + } + + if ( shouldDelete ) + { + delete this; + } + } + + private: + ////////////////////////////////////////////////////////////////////////// + mutable CountType count_; }; @@ -54,22 +104,18 @@ class counted_base +template< typename CountType, bool NeedsLocking > inline void intrusive_ptr_add_ref( - const ::boost::fsm::detail::counted_base * pBase ) + const ::boost::fsm::detail::counted_base< CountType, NeedsLocking > * pBase ) { - ++pBase->count_; + pBase->add_ref(); } +template< typename CountType, bool NeedsLocking > inline void intrusive_ptr_release( - const ::boost::fsm::detail::counted_base * pBase ) + const ::boost::fsm::detail::counted_base< CountType, NeedsLocking > * pBase ) { - BOOST_ASSERT( pBase->count_ > 0 ); - --pBase->count_; - - if ( pBase->count_ == 0 ) - { - delete pBase; - } + pBase->release(); } diff --git a/include/boost/statechart/detail/leaf_state.hpp b/include/boost/statechart/detail/leaf_state.hpp index 1cfa450..1069a8c 100644 --- a/include/boost/statechart/detail/leaf_state.hpp +++ b/include/boost/statechart/detail/leaf_state.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_FSM_LEAF_STATE_HPP_INCLUDED #define BOOST_FSM_LEAF_STATE_HPP_INCLUDED ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied @@ -14,14 +14,6 @@ -#ifdef _MSC_VER -// these appear with warning level 4 only -#pragma warning( disable: 4511 ) // copy constructor could not be generated -#pragma warning( disable: 4512 ) // assignment operator could not be generated -#endif - - - namespace boost { namespace fsm @@ -52,7 +44,8 @@ class leaf_state : public universal_state< StateList > listPosition_ = listPosition; } - virtual void remove_from_state_list( StateList & states ) + virtual void remove_from_state_list( + StateList & states, typename StateList::iterator & ) { // Because the list owns the leaf_state, this leads to the immediate // termination of this state. diff --git a/include/boost/statechart/detail/node_state.hpp b/include/boost/statechart/detail/node_state.hpp index b08f471..f7dcbec 100644 --- a/include/boost/statechart/detail/node_state.hpp +++ b/include/boost/statechart/detail/node_state.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_FSM_NODE_STATE_HPP_INCLUDED #define BOOST_FSM_NODE_STATE_HPP_INCLUDED ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied @@ -12,13 +12,7 @@ #include - - -#ifdef _MSC_VER -// these appear with warning level 4 only -#pragma warning( disable: 4511 ) // copy constructor could not be generated -#pragma warning( disable: 4512 ) // assignment operator could not be generated -#endif +#include // BOOST_ASSERT @@ -32,7 +26,7 @@ namespace detail ////////////////////////////////////////////////////////////////////////////// -template< orthogonal_position_type noOfOrthogonalStates, class StateList > +template< orthogonal_position_type noOfOrthogonalRegions, class StateList > class node_state : public universal_state< StateList > { typedef universal_state< StateList > base_type; @@ -40,7 +34,7 @@ class node_state : public universal_state< StateList > ////////////////////////////////////////////////////////////////////////// node_state() { - for ( orthogonal_position_type pos = 0; pos < noOfOrthogonalStates; ++pos ) + for ( orthogonal_position_type pos = 0; pos < noOfOrthogonalRegions; ++pos ) { pInnerStates[ pos ] = 0; } @@ -56,36 +50,53 @@ class node_state : public universal_state< StateList > void add_inner_state( orthogonal_position_type position, universal_state< StateList > * pInnerState ) { - BOOST_ASSERT( ( position < noOfOrthogonalStates ) && + BOOST_ASSERT( ( position < noOfOrthogonalRegions ) && ( pInnerStates[ position ] == 0 ) ); pInnerStates[ position ] = pInnerState; } void remove_inner_state( orthogonal_position_type position ) { - BOOST_ASSERT( position < noOfOrthogonalStates ); + BOOST_ASSERT( position < noOfOrthogonalRegions ); pInnerStates[ position ] = 0; } - virtual void remove_from_state_list( StateList & states ) + virtual void remove_from_state_list( + StateList & states, typename StateList::iterator & pUnstableState ) { - // Destroy inner states in the reverse order of construction - for ( universal_state< StateList > ** pState = - &pInnerStates[ noOfOrthogonalStates ]; - pState > &pInnerStates[ 0 ]; --pState ) + if ( ( pUnstableState != states.end() ) && + ( get_pointer( *pUnstableState ) == this ) ) { - // An inner orthogonal state might have been terminated long before, - // that's why we have to check for 0 pointers - if ( *( pState - 1 ) != 0 ) + for ( universal_state< StateList > ** pState = &pInnerStates[ 0 ]; + pState != &pInnerStates[ noOfOrthogonalRegions ]; ++pState ) { - ( *( pState - 1 ) )->remove_from_state_list( states ); + BOOST_ASSERT( *pState == 0 ); + } + + states.erase( pUnstableState ); + pUnstableState = states.end(); + } + else + { + // Destroy inner states in the reverse order of construction + for ( universal_state< StateList > ** pState = + &pInnerStates[ noOfOrthogonalRegions ]; + pState != &pInnerStates[ 0 ]; --pState ) + { + // An inner orthogonal state might have been terminated long before, + // that's why we have to check for 0 pointers + if ( *( pState - 1 ) != 0 ) + { + ( *( pState - 1 ) )->remove_from_state_list( + states, pUnstableState ); + } } } } private: ////////////////////////////////////////////////////////////////////////// - universal_state< StateList > * pInnerStates[ noOfOrthogonalStates ]; + universal_state< StateList > * pInnerStates[ noOfOrthogonalRegions ]; }; diff --git a/include/boost/statechart/detail/event_handler.hpp b/include/boost/statechart/detail/reaction.hpp similarity index 83% rename from include/boost/statechart/detail/event_handler.hpp rename to include/boost/statechart/detail/reaction.hpp index 5615704..a0e050e 100644 --- a/include/boost/statechart/detail/event_handler.hpp +++ b/include/boost/statechart/detail/reaction.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_FSM_EVENT_HANDLER_HPP_INCLUDED #define BOOST_FSM_EVENT_HANDLER_HPP_INCLUDED ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied @@ -10,6 +10,10 @@ +#include + + + namespace boost { namespace fsm @@ -29,16 +33,16 @@ namespace detail ////////////////////////////////////////////////////////////////////////////// template< class Event > -class event_handler +class reaction { protected: ////////////////////////////////////////////////////////////////////////// - event_handler() {} - ~event_handler() {} + reaction() {} + ~reaction() {} private: ////////////////////////////////////////////////////////////////////////// - virtual bool handle_event( const Event & evt ) = 0; + virtual result react( const Event & toEvent ) = 0; friend class event< Event >; }; diff --git a/include/boost/statechart/detail/state_base.hpp b/include/boost/statechart/detail/state_base.hpp index 7e38f4e..ef3527e 100644 --- a/include/boost/statechart/detail/state_base.hpp +++ b/include/boost/statechart/detail/state_base.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_FSM_STATE_BASE_HPP_INCLUDED #define BOOST_FSM_STATE_BASE_HPP_INCLUDED ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied @@ -9,15 +9,17 @@ ////////////////////////////////////////////////////////////////////////////// + #include -#include -#include +#include // boost::noncopyable +#include // BOOST_ASSERT -#ifdef _MSC_VER -// these appear with warning level 4 only +#ifdef BOOST_MSVC +// We permanently turn off the following level 4 warnings because users will +// have to do so themselves anyway if we turn them back on #pragma warning( disable: 4511 ) // copy constructor could not be generated #pragma warning( disable: 4512 ) // assignment operator could not be generated #endif @@ -28,13 +30,27 @@ namespace boost { namespace fsm { + + + +class event_base; + + + namespace detail { +typedef unsigned char orthogonal_position_type; + + + ////////////////////////////////////////////////////////////////////////////// -class state_base : public counted_base, noncopyable +class state_base : private noncopyable, + // Derived class objects will be created, handled and destroyed by one and + // the same thread --> locking is not necessary + public counted_base< orthogonal_position_type, false > { public: ////////////////////////////////////////////////////////////////////////// @@ -47,10 +63,35 @@ class state_base : public counted_base, noncopyable protected: ////////////////////////////////////////////////////////////////////////// - state_base() : pOuterState_( 0 ) + state_base() : + reactionEnabled_( false ), + deferredEvents_( false ), + pOuterState_( 0 ) { } + void reaction_initiated() + { + // This assert fails when you try to call a reaction function outside + // an event handler or when you try to call two reaction functions + // inside an event handler. + // Every event handler must return the result of exactly one reaction + // function call (forward_event, discard_event, defer_event, transit, + // terminate) + BOOST_ASSERT( reactionEnabled_ ); + reactionEnabled_ = false; + } + + void defer_event() + { + deferredEvents_ = true; + } + + bool deferred_events() const + { + return deferredEvents_; + } + void set_context( state_base * pOuterState ) { // Context must only be set once @@ -67,6 +108,15 @@ class state_base : public counted_base, noncopyable private: ////////////////////////////////////////////////////////////////////////// + friend class ::boost::fsm::event_base; + + void enable_reaction() + { + reactionEnabled_ = true; + } + + bool reactionEnabled_; + bool deferredEvents_; // Storing another pointer to our outer state looks like a bit of a waste // but the alternatives are not really appealing either. To begin with, // there is a tiny difference between the two pointers: The subclass @@ -74,15 +124,6 @@ class state_base : public counted_base, noncopyable // or the state machine. This pointer however only points to our outer // state _if_ there is one. It is 0 if the outer context of this state // is the state machine itself. - // Doing away with the pointer in the subclass would mean the following: - // - Having a ref-counted smartpointer as a member of this class - // - The smart pointer would have to point either to a common base class - // of state and state_machine or void. The former looks rather - // artificial (how would you name the base class?) and the latter - // has a runtime overhead attached to it (deleting the referenced - // object of a shared_ptr< void > involves an additional virtual call) - // - The smart pointer must be given a no-op deleter in case our context - // is the state machine state_base * pOuterState_; }; diff --git a/include/boost/statechart/detail/universal_state.hpp b/include/boost/statechart/detail/universal_state.hpp index e5665fc..71c657f 100644 --- a/include/boost/statechart/detail/universal_state.hpp +++ b/include/boost/statechart/detail/universal_state.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_FSM_UNIVERSAL_STATE_HPP_INCLUDED #define BOOST_FSM_UNIVERSAL_STATE_HPP_INCLUDED ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied @@ -14,14 +14,6 @@ -#ifdef _MSC_VER -// these appear with warning level 4 only -#pragma warning( disable: 4511 ) // copy constructor could not be generated -#pragma warning( disable: 4512 ) // assignment operator could not be generated -#endif - - - namespace boost { namespace fsm @@ -52,7 +44,8 @@ class universal_state : public state_base // CAUTION: The following declarations should be private. // They are only public because many compilers lack template friends. ////////////////////////////////////////////////////////////////////////// - virtual void remove_from_state_list( StateList & states ) = 0; + virtual void remove_from_state_list( + StateList & states, typename StateList::iterator & pUnstableState ) = 0; }; diff --git a/include/boost/statechart/event.hpp b/include/boost/statechart/event.hpp index c3ca832..c64bfd9 100644 --- a/include/boost/statechart/event.hpp +++ b/include/boost/statechart/event.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_FSM_EVENT_HPP_INCLUDED #define BOOST_FSM_EVENT_HPP_INCLUDED ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied @@ -10,18 +10,11 @@ -#include -#include -#include +#include +#include +#include -#include - - -#ifdef _MSC_VER -// these appear with warning level 4 only -#pragma warning( disable: 4511 ) // copy constructor could not be generated -#pragma warning( disable: 4512 ) // assignment operator could not be generated -#endif +#include // boost::polymorphic_downcast @@ -32,8 +25,12 @@ namespace fsm +class state_base; + + + template< class Derived > -class event : public detail::event_base +class event : public event_base { protected: ////////////////////////////////////////////////////////////////////////// @@ -41,29 +38,34 @@ class event : public detail::event_base private: ////////////////////////////////////////////////////////////////////////// - virtual bool send_to( detail::state_base & toState ) const + virtual result send( detail::state_base & toState ) const { - bool wasConsumed( false ); + result reactionResult = do_forward_event; detail::state_base * pCurrentState( &toState ); - while ( !wasConsumed && ( pCurrentState != 0 ) ) + while ( ( reactionResult == do_forward_event ) && + ( pCurrentState != 0 ) ) { - detail::event_handler< Derived > * const pReceiver = - dynamic_cast< detail::event_handler< Derived > * >( pCurrentState ); + detail::reaction< Derived > * const pReceiver = + dynamic_cast< detail::reaction< Derived > * >( pCurrentState ); if ( pReceiver != 0 ) { - wasConsumed = pReceiver->handle_event( + enable_reaction( *pCurrentState ); + // the following statement could delete pCurrentState! + reactionResult = pReceiver->react( *polymorphic_downcast< const Derived * >( this ) ); } - if ( !wasConsumed ) + if ( reactionResult == do_forward_event ) { + // we can only safely access pCurrentState if the handler did not + // return do_discard_event! pCurrentState = pCurrentState->outer_state_ptr(); } } - return wasConsumed; + return reactionResult; } }; diff --git a/include/boost/statechart/detail/event_base.hpp b/include/boost/statechart/event_base.hpp similarity index 70% rename from include/boost/statechart/detail/event_base.hpp rename to include/boost/statechart/event_base.hpp index ba2fca0..f62a8f1 100644 --- a/include/boost/statechart/detail/event_base.hpp +++ b/include/boost/statechart/event_base.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_FSM_EVENT_BASE_HPP_INCLUDED #define BOOST_FSM_EVENT_BASE_HPP_INCLUDED ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied @@ -10,12 +10,18 @@ +#include +#include + +#include + #include -#ifdef _MSC_VER -// these appear with warning level 4 only +#ifdef BOOST_MSVC +// We permanently turn off the following level 4 warnings because users will +// have to do so themselves anyway if we turn them back on #pragma warning( disable: 4511 ) // copy constructor could not be generated #pragma warning( disable: 4512 ) // assignment operator could not be generated #endif @@ -26,17 +32,11 @@ namespace boost { namespace fsm { -namespace detail -{ - - - -class state_base; ////////////////////////////////////////////////////////////////////////////// -class event_base : noncopyable +class event_base : private noncopyable, public detail::counted_base< unsigned int > { public: ////////////////////////////////////////////////////////////////////////// @@ -46,17 +46,21 @@ class event_base : noncopyable ////////////////////////////////////////////////////////////////////////// event_base() {} + static void enable_reaction( detail::state_base & fromState ) + { + fromState.enable_reaction(); + } + public: ////////////////////////////////////////////////////////////////////////// // CAUTION: The following declarations should be private. // They are only public because many compilers lack template friends. ////////////////////////////////////////////////////////////////////////// - virtual bool send_to( state_base & toState ) const = 0; + virtual result send( detail::state_base & toState ) const = 0; }; -} // namespace detail } // namespace fsm } // namespace boost diff --git a/include/boost/statechart/simple_exception_translator.hpp b/include/boost/statechart/exception_translator.hpp similarity index 57% rename from include/boost/statechart/simple_exception_translator.hpp rename to include/boost/statechart/exception_translator.hpp index b58c21d..5a49ad1 100644 --- a/include/boost/statechart/simple_exception_translator.hpp +++ b/include/boost/statechart/exception_translator.hpp @@ -1,7 +1,7 @@ -#ifndef BOOST_FSM_SIMPLE_EXCEPTION_TRANSLATOR_HPP_INCLUDED -#define BOOST_FSM_SIMPLE_EXCEPTION_TRANSLATOR_HPP_INCLUDED +#ifndef BOOST_FSM_EXCEPTION_TRANSLATOR_HPP_INCLUDED +#define BOOST_FSM_EXCEPTION_TRANSLATOR_HPP_INCLUDED ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied @@ -10,9 +10,8 @@ -#include #include -#include +#include @@ -23,15 +22,17 @@ namespace fsm -class simple_exception_event : public event< simple_exception_event > {}; +class exception_thrown : public event< exception_thrown > {}; -template< class ExceptionEvent = simple_exception_event > -struct simple_exception_translator +struct exception_translator { template< class Action, class ExceptionEventHandler > - bool operator()( Action action, ExceptionEventHandler eventHandler ) + result operator()( + Action action, + ExceptionEventHandler eventHandler, + result handlerSuccessResult ) { try { @@ -39,12 +40,12 @@ struct simple_exception_translator } catch ( ... ) { - if ( !eventHandler( ExceptionEvent() ) ) + if ( !eventHandler( exception_thrown() ) ) { throw; } - return true; + return handlerSuccessResult; } } }; diff --git a/include/boost/statechart/result.hpp b/include/boost/statechart/result.hpp new file mode 100644 index 0000000..563ed7d --- /dev/null +++ b/include/boost/statechart/result.hpp @@ -0,0 +1,34 @@ +#ifndef BOOST_FSM_RESULT_HPP_INCLUDED +#define BOOST_FSM_RESULT_HPP_INCLUDED +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +////////////////////////////////////////////////////////////////////////////// + + + +namespace boost +{ +namespace fsm +{ + + + +enum result +{ + do_forward_event, + do_discard_event, + do_defer_event +}; + + + +} // namespace fsm +} // namespace boost + + + +#endif diff --git a/include/boost/statechart/simple_state.hpp b/include/boost/statechart/simple_state.hpp index 67a2c64..5da3dcb 100644 --- a/include/boost/statechart/simple_state.hpp +++ b/include/boost/statechart/simple_state.hpp @@ -1,20 +1,19 @@ #ifndef BOOST_FSM_SIMPLE_STATE_HPP_INCLUDED #define BOOST_FSM_SIMPLE_STATE_HPP_INCLUDED ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied // warranty, and with no claim as to its suitability for any purpose. ////////////////////////////////////////////////////////////////////////////// -//#define BOOST_MPL_USE_NEXT_INTERNALLY #include #include -#include +#include #include #include @@ -42,11 +41,10 @@ #include #include -#include #include -#include -#include -#include +#include // boost::polymorphic_downcast +#include // BOOST_STATIC_CONSTANT +#include // BOOST_ASSERT @@ -69,7 +67,7 @@ struct make_list : public mpl::apply_if< using namespace mpl::placeholders; -template< class Derived, class Context, class Transitions, class InnerInitial > +template< class Derived, class Context, class Reactions, class InnerInitial > struct state_base_type { private: @@ -84,13 +82,14 @@ struct state_base_type mpl::size< inner_initial_list >::type::value, typename Context::state_list_type > > >::type base; - typedef typename detail::make_list< Transitions >::type transition_list; + typedef typename detail::make_list< Reactions >::type reaction_list; typedef typename mpl::transform< - transition_list, mpl::apply1< _, Derived > >::type handler_list; + reaction_list, mpl::apply1< _, Derived > >::type handler_list; public: typedef typename mpl::fold< - handler_list, base, mpl::inherit2< _1, _2 > >::type type; + typename mpl::reverse< typename mpl::push_front< handler_list, base >::type >::type, + mpl::empty_base, mpl::inherit2< _1, _2 > >::type type; }; @@ -235,23 +234,23 @@ struct constructor : -typedef detail::empty_list no_transitions; +typedef detail::empty_list no_reactions; // Base class for all states template< class Derived, class Context, // either an outer state or a state_machine - class Transitions = no_transitions, + class Reactions = no_reactions, class InnerInitial = detail::empty_list > // initial inner state -class simple_state : public mpl::aux::msvc_eti_base< - typename detail::state_base_type< +class simple_state : public // mpl::aux::msvc_eti_base< + /*typename*/ detail::state_base_type< Derived, typename Context::inner_context_type, - Transitions, InnerInitial >::type >::type + Reactions, InnerInitial >::type //>::type { typedef typename detail::state_base_type< Derived, typename Context::inner_context_type, - Transitions, InnerInitial >::type base_type; + Reactions, InnerInitial >::type base_type; public: ////////////////////////////////////////////////////////////////////////// @@ -259,16 +258,18 @@ class simple_state : public mpl::aux::msvc_eti_base< BOOST_STATIC_CONSTANT( detail::orthogonal_position_type, orthogonal_position = Context::inner_orthogonal_position ); - typedef typename context_type::event_queue_type event_queue_type; + typedef typename context_type::event_ptr_type event_ptr_type; - template< detail::orthogonal_position_type InnerOrthogonalPosition > + typedef simple_state my_base; + + template< detail::orthogonal_position_type innerOrthogonalPosition > struct orthogonal { // TODO: check that this state really has such an inner orthogonal state BOOST_STATIC_CONSTANT( detail::orthogonal_position_type, - inner_orthogonal_position = InnerOrthogonalPosition ); + inner_orthogonal_position = innerOrthogonalPosition ); typedef Derived inner_context_type; }; @@ -288,22 +289,43 @@ class simple_state : public mpl::aux::msvc_eti_base< return context_impl( static_cast< OtherContext * >( 0 ) ); } - // Initiates a transition to next_state (without transition action). - template< class NextState > - bool transit_to() + + result discard_event() { - return transit_to_impl< NextState >( detail::no_transition_function() ); + state_base::reaction_initiated(); + return do_discard_event; + } + + result forward_event() + { + state_base::reaction_initiated(); + return do_forward_event; + } + + result defer_event() + { + state_base::reaction_initiated(); + defer_event(); + return do_defer_event; + } + + // Initiates a transition to DestinationState (without transition action). + template< class DestinationState > + result transit() + { + return transit_impl< DestinationState >( + detail::no_transition_function() ); } - // Initiates a transition to next_state with a transition action. The - // transition action must be a member of the innermost common context + // Initiates a transition to DestinationState with a transition action. + // The transition action must be a member of the innermost common context // or of one of its contexts. - template< class NextState, class TransitionContext, class Event > - bool transit_to( + template< class DestinationState, class TransitionContext, class Event > + result transit( void ( TransitionContext::*pTransitionAction )( const Event & ), const Event & evt ) { - return transit_to_impl< NextState >( + return transit_impl< DestinationState >( detail::transition_function< TransitionContext, Event >( pTransitionAction, evt ) ); } @@ -311,14 +333,15 @@ class simple_state : public mpl::aux::msvc_eti_base< // Terminates this state. Depending on whether there are other orthogonal // states present this may or may not lead to the whole statemachine being // terminated. - bool terminate() + result terminate() { + state_base::reaction_initiated(); top_context().terminate( *this ); - return true; + return do_discard_event; } - void post_event( const typename event_queue_type::value_type & pEvent ) + void post_event( const event_ptr_type & pEvent ) { top_context().post_event( pEvent ); } @@ -330,8 +353,7 @@ class simple_state : public mpl::aux::msvc_eti_base< virtual ~simple_state() { // As a result of a throwing derived class constructor, this destructor - // can be called before the context is set. There's nothing to do - // in this case. + // can be called before the context is set. if ( get_pointer( pContext_ ) != 0 ) { pContext_->remove_inner_state( orthogonal_position ); @@ -362,6 +384,7 @@ class simple_state : public mpl::aux::msvc_eti_base< top_context_type & top_context() { + BOOST_ASSERT( get_pointer( pContext_ ) != 0 ); return pContext_->top_context(); } @@ -435,6 +458,7 @@ class simple_state : public mpl::aux::msvc_eti_base< const OtherContextPtr & context_ptr_impl( const OtherContextPtr & ) const { + BOOST_ASSERT( get_pointer( pContext_ ) != 0 ); return pContext_->context_ptr< OtherContextPtr >(); } @@ -442,6 +466,7 @@ class simple_state : public mpl::aux::msvc_eti_base< const context_ptr_type & context_ptr_impl( const context_ptr_type & ) const { + BOOST_ASSERT( get_pointer( pContext_ ) != 0 ); return pContext_; } @@ -449,6 +474,7 @@ class simple_state : public mpl::aux::msvc_eti_base< template< class OtherContext > OtherContext & context_impl( OtherContext * ) { + BOOST_ASSERT( get_pointer( pContext_ ) != 0 ); return pContext_->context< OtherContext >(); } @@ -461,6 +487,7 @@ class simple_state : public mpl::aux::msvc_eti_base< template< class OtherContext > const OtherContext & context_impl( OtherContext * ) const { + BOOST_ASSERT( get_pointer( pContext_ ) != 0 ); const OtherContext & constContext( *pContext_ ); return constContext.context< OtherContext >(); } @@ -472,12 +499,13 @@ class simple_state : public mpl::aux::msvc_eti_base< } - template< class NextState, class TransitionAction > - bool transit_to_impl( const TransitionAction & transitionAction ) + template< class DestinationState, class TransitionAction > + result transit_impl( const TransitionAction & transitionAction ) { + base_type::reaction_initiated(); typedef typename mpl::find_if< context_type_list, - mpl::contains< typename NextState::context_type_list, _ > + mpl::contains< typename DestinationState::context_type_list, _ > >::type common_context_iter; typedef typename mpl::deref< common_context_iter >::type common_context_type; @@ -502,18 +530,19 @@ class simple_state : public mpl::aux::msvc_eti_base< typedef typename mpl::reverse< mpl::push_front< mpl::erase< - typename NextState::context_type_list, + typename DestinationState::context_type_list, typedef typename mpl::find< - typename NextState::context_type_list, + typename DestinationState::context_type_list, common_context_type >::type, - typename mpl::end< typename NextState::context_type_list >::type + typename mpl::end< typename DestinationState::context_type_list >::type >::type, - NextState + DestinationState >::type >::type context_list; - detail::constructor< context_list, top_context_type >::construct( pCommonContext, topContext ); + detail::constructor< context_list, top_context_type >::construct( + pCommonContext, topContext ); - return true; + return do_discard_event; } diff --git a/include/boost/statechart/state.hpp b/include/boost/statechart/state.hpp index 6fe3368..2c27434 100644 --- a/include/boost/statechart/state.hpp +++ b/include/boost/statechart/state.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_FSM_STATE_HPP_INCLUDED #define BOOST_FSM_STATE_HPP_INCLUDED ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied @@ -23,12 +23,12 @@ namespace fsm template< class Derived, class Context, // either an outer state or a state_machine - class Transitions = no_transitions, + class Reactions = no_reactions, class InnerInitial = detail::empty_list > // initial inner state class state : - public simple_state< Derived, Context, Transitions, InnerInitial > + public simple_state< Derived, Context, Reactions, InnerInitial > { - typedef simple_state< Derived, Context, Transitions, InnerInitial > + typedef simple_state< Derived, Context, Reactions, InnerInitial > base_type; protected: diff --git a/include/boost/statechart/state_machine.hpp b/include/boost/statechart/state_machine.hpp index 2377c6f..bdb2c53 100644 --- a/include/boost/statechart/state_machine.hpp +++ b/include/boost/statechart/state_machine.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_FSM_STATE_MACHINE_HPP_INCLUDED #define BOOST_FSM_STATE_MACHINE_HPP_INCLUDED ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied @@ -11,31 +11,43 @@ #include -#include -#include + +#include +#include +#include #include -#include #include -#include #include -#include -#include -#include +#include // boost::polymorphic_downcast +#include // boost::noncopyable +#include // BOOST_ASSERT +#include // BOOST_STATIC_CONSTANT -#include +#ifdef BOOST_MSVC +#pragma warning( push ) +#pragma warning( disable: 4702 ) // unreachable code (in release mode only) +#endif -#ifdef _MSC_VER -// these appear with warning level 4 only -#pragma warning( disable: 4702 ) // unreachable code +#include + +#ifdef BOOST_MSVC +#pragma warning( pop ) +#endif + +#include // std::allocator +#include // std::bad_cast + + + +#ifdef BOOST_MSVC +// We permanently turn off the following level 4 warnings because users will +// have to do so themselves anyway if we turn them back on #pragma warning( disable: 4511 ) // copy constructor could not be generated #pragma warning( disable: 4512 ) // assignment operator could not be generated #endif -#include -#include - namespace boost @@ -49,33 +61,31 @@ namespace detail template< class StateList > class universal_state; -template< orthogonal_position_type noOfOrthogonalStates, class StateList > +template< orthogonal_position_type noOfOrthogonalRegions, class StateList > class node_state; template< class StateList > class leaf_state; -class event_base; - class send_function { public: ////////////////////////////////////////////////////////////////////////// send_function( - const detail::event_base & evt, detail::state_base & toState + const event_base & evt, state_base & toState ) : evt_( evt ), toState_( toState ) { } - bool operator()() + result operator()() { - return evt_.send_to( toState_ ); + return evt_.send( toState_ ); } private: ////////////////////////////////////////////////////////////////////////// - const detail::event_base & evt_; - detail::state_base & toState_; + const event_base & evt_; + state_base & toState_; }; @@ -89,22 +99,21 @@ class send_function // Some function names were derived from a state machine by Aleksey Gurtovoy. template< class Derived, class InitialState, - class ExceptionTranslator = simple_exception_translator<>, - class EventQueue = - std::queue< shared_ptr< const detail::event_base > >, - class StateList = std::list< intrusive_ptr< detail::state_base > > > -class state_machine : noncopyable + class ExceptionTranslator = exception_translator, + class Allocator = std::allocator< void > > +class state_machine : private noncopyable { public: ////////////////////////////////////////////////////////////////////////// - typedef EventQueue event_queue_type; + typedef intrusive_ptr< event_base > event_ptr_type; void initiate() { terminate(); translator_( initial_construct_function( *this ), - exception_event_handler( *this ) ); + exception_event_handler( *this ), + do_discard_event ); process_queued_events(); } @@ -114,7 +123,7 @@ class state_machine : noncopyable } - void process_event( const detail::event_base & evt ) + void process_event( const event_base & evt ) { send_event( evt ); process_queued_events(); @@ -144,13 +153,13 @@ class state_machine : noncopyable { const State * pResult = 0; - for ( StateList::const_iterator pCurrentLeafState = + for ( state_list_type::const_iterator pCurrentLeafState = currentStates_.begin(); ( pCurrentLeafState != currentStates_.end() ) && ( pResult == 0 ); ++pCurrentLeafState ) { const detail::state_base * pCurrentState( - ( *pCurrentLeafState ).get() ); + get_pointer( *pCurrentLeafState ) ); while ( ( pCurrentState != 0 ) && ( ( pResult = dynamic_cast< const State * >( pCurrentState ) ) == 0 ) ) @@ -165,7 +174,7 @@ class state_machine : noncopyable protected: ////////////////////////////////////////////////////////////////////////// state_machine() : - pIncompleteState_( currentStates_.end() ) + pUnstableState_( currentStates_.end() ) { } @@ -185,7 +194,10 @@ class state_machine : noncopyable typedef Derived top_context_type; typedef Derived * inner_context_ptr_type; - typedef StateList state_list_type; + typedef intrusive_ptr< detail::state_base > state_ptr_type; + typedef std::list< + state_ptr_type, + typename Allocator::rebind< state_ptr_type >::other > state_list_type; typedef mpl::clear< mpl::list<> >::type context_type_list; @@ -216,48 +228,45 @@ class state_machine : noncopyable void terminate( state_machine & ) { - // there is no longer any use for possibly remaining events - eventQueue_ = EventQueue(); + pUnstableState_ = currentStates_.end(); currentStates_.clear(); - pIncompleteState_ = currentStates_.end(); + // there is no longer any use for possibly remaining events + eventQueue_.clear(); } - void terminate( detail::universal_state< StateList > & theState ) + void terminate( detail::universal_state< state_list_type > & theState ) { - // If we receive a terminate request while the machine is not stable - // then this can only have happened as a result of an exception and we - // unconditionally terminate the incomplete state. - if ( !stable() ) - { - currentStates_.erase( pIncompleteState_ ); - pIncompleteState_ = currentStates_.end(); - } + if ( currentStates_.size() == 1 ) + { + // The following optimization is only correct when there are no + // orthogonal states. + currentStates_.clear(); + } + else + { + // This would work for all cases, but is unnecessarily inefficient + // when there are no orthogonal states. + theState.remove_from_state_list( currentStates_, pUnstableState_ ); + } + } - if ( currentStates_.size() == 1 ) - { - // The following optimization is only correct when there are no - // orthogonal states. - currentStates_.clear(); - } - else - { - // This would work for all cases, but is unnecessarily inefficient - // when there are no orthogonal states. - theState.remove_from_state_list( currentStates_ ); - } + void post_event( const event_ptr_type & pEvent ) + { + BOOST_ASSERT( get_pointer( pEvent ) != 0 ); + eventQueue_.push_back( pEvent ); } template< class State > void add( const intrusive_ptr< State > & pState ) { - if ( stable() ) + if ( machine_status() == unstable ) { - pIncompleteState_ = - currentStates_.insert( currentStates_.end(), pState ); + *pUnstableState_ = pState; } else { - *pIncompleteState_ = pState; + pUnstableState_ = + currentStates_.insert( currentStates_.end(), pState ); } add_impl( *pState ); @@ -265,18 +274,12 @@ class state_machine : noncopyable void add( state_machine * ) { - BOOST_ASSERT( stable() && ( currentStates_.size() == 0 ) ); - } - - void post_event( const typename event_queue_type::value_type & pEvent ) - { - BOOST_ASSERT( get_pointer( pEvent ) != 0 ); - eventQueue_.push( pEvent ); + BOOST_ASSERT( machine_status() == no_state ); } void add_inner_state( detail::orthogonal_position_type position, - detail::universal_state< StateList > * ) + detail::universal_state< state_list_type > * ) { BOOST_ASSERT( position == 0 ); position; @@ -290,6 +293,10 @@ class state_machine : noncopyable private: // implementation ////////////////////////////////////////////////////////////////////////// + typedef std::list< + event_ptr_type, + typename Allocator::rebind< event_ptr_type >::other > event_queue_type; + void initial_construct() { InitialState::deep_construct( @@ -307,10 +314,10 @@ class state_machine : noncopyable { } - bool operator()() + result operator()() { machine_.initial_construct(); - return true; // there is nothing to be consumed + return do_discard_event; // there is nothing to be consumed } private: @@ -325,25 +332,35 @@ class state_machine : noncopyable const ExceptionEvent & exceptionEvent, detail::state_base * pCurrentState ) { + const machine_status_enum status = machine_status(); + + if ( status == no_state ) + { + // there is not state that could handle the exception -> bail out + return false; + } + // If we are stable, an event handler has thrown. // Otherwise, either a state constructor or a transition action // has thrown and the state machine is now in an invalid state. // This situation can be resolved by the exception event handler // function by orderly transiting to another state or terminating. - // As a result of this, pCurrentIncompleteState_ must point to - // currentStates_.end() after processing the exception event. + // As a result of this, the machine must not be unstable when this + // function is left. detail::state_base * const pHandlingState = - stable() ? pCurrentState : &incomplete_state(); + status == stable ? pCurrentState : &unstable_state(); BOOST_ASSERT( pHandlingState != 0 ); - // Deliberately ignore whether the event was consumed or not. Important - // is whether we are stable or not. - translator_( + const result reaction_result = translator_( detail::send_function( exceptionEvent, *pHandlingState ), - exception_event_handler( *this, pCurrentState ) ); + exception_event_handler( *this, pCurrentState ), + do_discard_event ); - return stable(); + // TODO: defer event here + + return ( reaction_result != do_forward_event ) && + ( machine_status() != unstable ); } class exception_event_handler @@ -373,22 +390,24 @@ class state_machine : noncopyable friend exception_event_handler; - bool send_event( const detail::event_base & evt ) + void send_event( const event_base & evt ) { - BOOST_ASSERT( stable() ); + BOOST_ASSERT( machine_status() != unstable ); - bool wasConsumed = false; - StateList::iterator pState = currentStates_.begin(); + result reaction_result = do_forward_event; + state_list_type::iterator pState = currentStates_.begin(); - while ( !wasConsumed && ( pState != currentStates_.end() ) ) + while ( ( reaction_result == do_forward_event ) && + ( pState != currentStates_.end() ) ) { try { // CAUTION: The following statement could modify our state list! // We must not continue iterating, if the event was consumed - wasConsumed = translator_( + reaction_result = translator_( detail::send_function( evt, **pState ), - exception_event_handler( *this, get_pointer( *pState ) ) ); + exception_event_handler( *this, get_pointer( *pState ) ), + do_discard_event ); } catch ( ... ) { @@ -396,54 +415,71 @@ class state_machine : noncopyable throw; } - if ( !wasConsumed ) + // TODO: defer event here + + if ( reaction_result == do_forward_event ) { - // we can only safely advance the iterator _if_ currentStates has + // we can only safely advance the iterator if currentStates has // not been modified! ++pState; } } - - return wasConsumed; } void process_queued_events() { while ( !eventQueue_.empty() ) { - const typename event_queue_type::value_type pCurrentEvent( - eventQueue_.front() ); - eventQueue_.pop(); + const event_ptr_type pCurrentEvent( eventQueue_.front() ); + eventQueue_.pop_front(); send_event( *pCurrentEvent ); } } - bool stable() const + enum machine_status_enum { - return pIncompleteState_ == currentStates_.end(); + no_state, + unstable, + stable + }; + + machine_status_enum machine_status() const + { + if ( currentStates_.empty() ) + { + return no_state; + } + else if ( pUnstableState_ != currentStates_.end() ) + { + return unstable; + } + else + { + return stable; + } } - detail::state_base & incomplete_state() + detail::state_base & unstable_state() { - BOOST_ASSERT( !stable() ); - return **pIncompleteState_; + BOOST_ASSERT( machine_status() == unstable ); + return **pUnstableState_; } - template< detail::orthogonal_position_type noOfOrthogonalStates, class StateList > + template< detail::orthogonal_position_type noOfOrthogonalRegions, class StateList > void add_impl( - const detail::node_state< noOfOrthogonalStates, StateList > & ) {} + const detail::node_state< noOfOrthogonalRegions, StateList > & ) {} template< class StateList > void add_impl( detail::leaf_state< StateList > & theState ) { - theState.set_list_position( pIncompleteState_ ); - pIncompleteState_ = currentStates_.end(); + theState.set_list_position( pUnstableState_ ); + pUnstableState_ = currentStates_.end(); } event_queue_type eventQueue_; state_list_type currentStates_; - typename state_list_type::iterator pIncompleteState_; + typename state_list_type::iterator pUnstableState_; ExceptionTranslator translator_; }; diff --git a/include/boost/statechart/terminator.hpp b/include/boost/statechart/termination.hpp similarity index 60% rename from include/boost/statechart/terminator.hpp rename to include/boost/statechart/termination.hpp index d359b1f..a6d0866 100644 --- a/include/boost/statechart/terminator.hpp +++ b/include/boost/statechart/termination.hpp @@ -1,7 +1,7 @@ -#ifndef BOOST_FSM_TERMINATOR_HPP_INCLUDED -#define BOOST_FSM_TERMINATOR_HPP_INCLUDED +#ifndef BOOST_FSM_TERMINATION_HPP_INCLUDED +#define BOOST_FSM_TERMINATION_HPP_INCLUDED ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied @@ -10,9 +10,9 @@ -#include +#include -#include +#include // boost::polymorphic_downcast @@ -26,10 +26,10 @@ namespace detail template< class Derived, class Event > -class terminator_handler : public event_handler< Event > +class termination_reaction : public reaction< Event > { private: - virtual bool handle_event( const Event & ) + virtual result react( const Event & ) { return polymorphic_downcast< Derived * >( this )->terminate(); } @@ -42,13 +42,13 @@ class terminator_handler : public event_handler< Event > template< class Event > -struct terminator +struct termination { - template< class Derived > - struct apply - { - typedef detail::terminator_handler< Derived, Event > type; - }; + template< class Derived > + struct apply + { + typedef detail::termination_reaction< Derived, Event > type; + }; }; diff --git a/include/boost/statechart/transition.hpp b/include/boost/statechart/transition.hpp index f4aea1e..52dc7a1 100644 --- a/include/boost/statechart/transition.hpp +++ b/include/boost/statechart/transition.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_FSM_TRANSITION_HPP_INCLUDED #define BOOST_FSM_TRANSITION_HPP_INCLUDED ////////////////////////////////////////////////////////////////////////////// -// (c) 2002 Andreas Huber, Zurich, Switzerland +// Copyright (c) 2002-2003 Andreas Huber Doenni, Switzerland // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied @@ -10,7 +10,7 @@ -#include +#include #include @@ -36,26 +36,26 @@ struct no_context template< class Derived, class Event, class Destination, class TransitionContext, void ( TransitionContext::*pTransitionAction )( const Event & ) > -class transition_handler : public event_handler< Event > +class transition_reaction : public reaction< Event > { private: - virtual bool handle_event( const Event & evt ) + virtual result react( const Event & toEvent ) { - return handle_event_impl< TransitionContext >( evt ); + return react_impl< TransitionContext >( toEvent ); } template< class TransitionContext > - bool handle_event_impl( const Event & evt ) + result react_impl( const Event & toEvent ) { return polymorphic_downcast< Derived * >( this )-> - transit_to< Destination >( pTransitionAction, evt ); + transit< Destination >( pTransitionAction, toEvent ); } template<> - bool handle_event_impl< no_context >( const Event & ) + result react_impl< no_context >( const Event & ) { return polymorphic_downcast< Derived * >( this )-> - transit_to< Destination >(); + transit< Destination >(); } }; @@ -74,7 +74,7 @@ struct transition template< class Derived > struct apply { - typedef detail::transition_handler< + typedef detail::transition_reaction< Derived, Event, Destination, TransitionContext, pTransitionAction > type; };