diff --git a/doc/acknowledgments.html b/doc/acknowledgments.html index 85b9a9d..601fd20 100644 --- a/doc/acknowledgments.html +++ b/doc/acknowledgments.html @@ -37,20 +37,20 @@

Special thanks go to:

Thanks for feedback and/or encouragement go to:

David Abrahams, Bohdan, Reece Dunn, Jeff Garland, Douglas Gregor, Gustavo -Guerra, Aleksey Gurtovoy, Chris Russell and Scott Woods.

+Guerra, Aleksey Gurtovoy, Chris Russell, Vincent N. Virgilio and Scott Woods.


Revised -12 October, 2003

+17 November, 2003

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

diff --git a/doc/configuration.html b/doc/configuration.html new file mode 100644 index 0000000..15defb6 --- /dev/null +++ b/doc/configuration.html @@ -0,0 +1,89 @@ + + + + + + + + +The boost::fsm library - Configuration + + + + + + + + + +
+

+ C++ Boost

+
+

The boost::fsm library

+

Configuration

+
+
+
+
Introduction
+
Debug mode compilation options
+
Release mode compilation + options
+
Application Defined Macros
+
+

Introduction

+

The library uses several configuration macros in +<boost/config.hpp>, +as well as one configuration macro meant to be supplied by the application. +Moreover, two commonly available compiler options also have an impact on the +available features.

+

Debug mode compilation options

+ +

Release mode compilation +options

+ +

Application Defined Macros

+

The following macro may be defined by an application using the library:

+ + + + + + + + + +
MacroMeaning
BOOST_FSM_USE_NATIVE_RTTIWhen defined, the library no longer uses its own + speed-optimized RTTI implementation. Instead, native C++ RTTI is employed. + This has the following effects:
    +
  • All states need to store one pointer less, leading to a best-case + state machine memory footprint reduction of about 15%
  • +
  • Custom state + type information is no longer available
  • +
  • Under most circumstances, dispatch speed degrades. This is because + native C++ RTTI values are retrieved through an additional indirection + on almost all platforms
  • +
+
+
+

Revised + 30 November, 2003 +

+

© Copyright Andreas Huber 2003.

+ + + + diff --git a/doc/definitions.html b/doc/definitions.html index d0c9c5a..5a9fe19 100644 --- a/doc/definitions.html +++ b/doc/definitions.html @@ -24,18 +24,49 @@
+

Introduction

+

The boost::fsm documentation uses a lot of terminology specific to state +machines. Most of it is equal to the one used in the UML specifications. This +document contains only definitions for terminology not used by UML.

+

Definitions

+
Context
+
Innermost common outer state
+
Innermost state
+
In-state reaction
+
Outermost state
Polymorphic events
+
Reaction
+
Unstable state
+
Unstable state machine
+

Context

+

The direct context of a state is either its outer state or the state +machine. In the latter case the state is an outermost state.

+

Innermost common outer state

+

The innermost common outer state of two states is the first direct or +indirect outer state that both states have in common. Also known as Least +Common Ancestor (UML).

+

Innermost state

+

An innermost state is a state that does not itself have inner states. Also +known as leaf state or simple state (UML). Note that +boost::fsm::simple_state is not a model of the UML simple state.

+

In-state reaction

+

An in-state reaction is a reaction that neither +exits nor enters any states. Also known as inner transition or internal +transition (UML).

+

Outermost state

+

An outermost state is a state that does not itself have outer states. Note +that an outermost state is different from a UML top state. A state machine can +have an arbitrary number of the former but only exactly one of the latter. +boost::fsm only supports outermost states.

Polymorphic events

-
-

An FSM library supports polymorphic events if events can inherit from - each other without restrictions and allows the definition of - reactions for leafs and nodes of the resulting event inheritance - tree.

-

Example (using a hypothetical fsm library, as boost::fsm does not support - polymorphic events):

-
struct EvButtonPressed : Event // node
+

An FSM library supports polymorphic events if events can inherit from each +other without restrictions and allows the definition of reactions for +leafs and nodes of the resulting event inheritance tree.

+

Example (using a hypothetical fsm library, as boost::fsm does not support +polymorphic events):

+
struct EvButtonPressed : Event // node
 {
   /* common button pressed properties */
 };
@@ -43,14 +74,51 @@
 struct EvPlayButtonPressed : EvButtonPressed {}; // leaf
 struct EvStopButtonPressed : EvButtonPressed {}; // leaf
 struct EvForwardButtonPressed : EvButtonPressed {}; // leaf
-

If a state machine needs to react whenever any button (including - the ones that may be added in the future) is pressed, a reaction for - EvButtonPressed can be defined.

- -
+

If a state machine needs to react whenever any button (including the +ones that may be added in the future) is pressed, a reaction for +EvButtonPressed can be defined.

+

Reaction

+

A reaction consists of all the side effects caused by the processing of one +event. Reactions can be categorized as follows:

+
    +
  1. In-state reaction
  2. +
  3. Event deferral
  4. +
  5. Transition
  6. +
  7. Termination, also known as transition to the final state (UML)
  8. +
+

Note that it is possible to mix a reaction of type 1 with one of the other +types (the in-state reaction is always executed first) but it is not possible +to mix a reaction of type 2-4 with anything else but type 1.

+

A reaction is always associated with exactly one state type and exactly one +event type.

+

Unstable state

+

A state is unstable from the moment when it has been entered until just +before its last direct inner state is entered.

+

Unstable state machine

+

A state machine is unstable if at least one of its currently active states +is unstable. This is the case during the following three operations:

+ +

Under normal circumstances a state machine has Run-To-Completion semantics, +that is, processing of an event is fully completed before the machine returns +to the client or before the next event is dequeued. Therefore, a state machine +is usually only unstable when it is busy processing an event and becomes +stable again right before it has finished processing the event. However, this +can not be guaranteed when either transition actions or entry actions fail +(exit actions cannot fail). Such a failure is reported by an event, which must +be processed while the state machine is unstable. However, exception event +processing rules ensure that a state machine cannot be unstable when it +returns to the client (see state_machine<>::process_event for +details).


Revised - 12 October, 2003 + 09 December, 2003

© Copyright Andreas Huber Dönni 2003.

diff --git a/doc/faq.html b/doc/faq.html index 291215a..621ea8e 100644 --- a/doc/faq.html +++ b/doc/faq.html @@ -42,7 +42,8 @@

Does boost::fsm support polymorphic events?

No. Although events can be derived from each other to write common code -only once, reactions can only be defined for most-derived events.

+only once, reactions can only be +defined for most-derived events.

Example:

template< class MostDerived >
 struct EvButtonPressed : fsm::event< MostDerived >
@@ -249,7 +250,7 @@ destructors of additional bases are called before recursion employed by state
 base destructors can alter the order of destruction.


Revised -12 October, 2003

+01 December, 2003

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

diff --git a/doc/index.html b/doc/index.html index 3c9ce59..b361a7a 100644 --- a/doc/index.html +++ b/doc/index.html @@ -53,14 +53,12 @@ include:

  • MSVC7.1 (the compiler coming with Visual Studio .NET 2003) with boost distribution 1.30.2
  • -

    The library is almost functionally complete. However, the following is -still in the making:

    +

    The library is functionally complete. However, the following is still in +the making:

      -
    1. A facility allowing users to attach arbitrary type information to states
    2. -
    3. A way to iterate over all current states of a machine.
    4. -
    5. Reference documentation
    6. +
    7. Finish reference documentation
    8. +
    9. gcc port
    10. Regression tests
    11. -
    12. Ports to other compilers
    13. Refactoring of the state_machine class template to reduce code size in projects with many different state machines
    @@ -69,13 +67,23 @@ still in the making:

    Contents

    Tutorial
    -
    Rationale
    -
    Definitions
    Frequently Asked Questions (FAQs)
    +
    Configuration
    +
    Definitions
    +
    Reference
    +
    Rationale
    Acknowledgments

    Change history

    +

    12 December 2003:

    +

    12 October 2003:


    Revised -12 October, 2003

    +12 December, 2003

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

    diff --git a/doc/rationale.html b/doc/rationale.html index 9ec3acd..bc4c4e9 100644 --- a/doc/rationale.html +++ b/doc/rationale.html @@ -48,8 +48,8 @@ are the result of the following requirements.

    boost::fsm should ...

      -
    1. be fully type-safe. Any type mismatches should be flagged with an error - at compile-time
    2. +
    3. be fully type-safe. Whenever possible, type mismatches should be flagged + with an error at compile-time
    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. @@ -94,8 +94,11 @@ frameworks:

      solution)
      http://www.ilogix.com/products/rhapsody/rhap_incplus.cfm
      -
      Fails to satisfy at least the requirements 2, 4, 5, 6, 8 (there is quite - a bit of error checking before code generation, though). + This might look like comparing apples with oranges. However, there is no + inherent reason why a code generator couldn't produce code that can easily + be understood and modified by humans. Fails to satisfy at least the + requirements 2, 4, 5, 6, 8 (there is quite a bit of error checking before + code generation, though).
    6. The framework accompanying the article "State Machine Design in C++"
      http://www.cuj.com/articles/2000/0005/0005f/0005f.htm?topic=articles
      @@ -150,8 +153,9 @@ However, due to its nature such a framework has a number of disadvantages when used to implement static machines:

      • No compile-time optimizations and validations can be made. For example, - boost::fsm determines the innermost common outer state (aka LCA, least - common ancestor) of the transition-source and destination state at compile + boost::fsm determines the + innermost common + outer state of the transition-source and destination state at compile time. Moreover, compile time checks ensure that the state machine is valid (e.g. that there are no transitions between orthogonal states).
      • Double dispatch must inevitably be implemented with some kind of a @@ -182,9 +186,9 @@ ignore the issue. 

        Consider the following state configuration:

        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 +active, 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 under @@ -229,11 +233,14 @@ code is simply boring and quite unnecessary.

      • The processing logic is as follows:
        • Exception events resulting from failed react functions - are sent to the current state.
        • + are sent to the innermost state + that was last visited during reaction + search
        • Exception events resulting from failed entry actions are sent to the - immediate outer state.
        • + outer state of the state that the machine tried to enter
        • Exception events resulting from failed transition actions are sent to - the innermost common outer state.
        • + the innermost + common outer state

        In the last two cases the state-machine is not in a stable state when the exception event is generated and leaving it there (e.g. by ignoring the @@ -245,25 +252,25 @@ code is simply boring and quite unnecessary.

        rethrown.

      Asynchronous state machines

      -

      The design of the asynchronous_state_machine<> and -worker<> class templates follow from the requirements:

      +

      The design of the asynchronous_state_machine and worker +class templates follow from the requirements:

      1. The user must be able to specify in which thread a particular machine will run:
        - The worker<> class template is not associated with a particular + The worker class template is not associated with a particular thread. Instead, users can choose to either call worker<>::operator() directly from the current thread or pass an appropriate function object to a new thread.
      2. An arbitrary number of state machines might run in the same thread:
        Multiple state machine objects can be constructed passing the same - worker<> object. The state machines will then share the same + worker object. The state machines will then share the same thread-safe queue and event loop.
      3. Out of the box, the boost::thread library should be employed. However, it should be possible to use any other threading library or run asynchronous machines on OS-less systems:
        In such cases, the locking and waiting logic can be fully customized by implementing a new class template that is interface-compatible with - worker<>.
      4. + worker.

      User actions: Member functions vs. function objects

      @@ -339,16 +346,18 @@ normally constitutes a bottleneck and the relative gap between handcrafted and boost::fsm machines also becomes much smaller than in the worst-case scenario.

      BitMachine measurements with more states and with different levels of optimization:

      - +
      - - + + + + BOOST_FSM_USE_NATIVE_RTTI defined @@ -450,7 +459,8 @@ Currently, there are exactly two options:

      1. By default, a speed-optimized internal implementation is employed
      2. The library can be instructed to use native C++ RTTI instead by defining - BOOST_FSM_USE_NATIVE_RTTI
      3. + + BOOST_FSM_USE_NATIVE_RTTI

      Just about the only reason to favor 2 is the fact that state objects need to store one pointer less, meaning that in the best case the memory footprint @@ -467,10 +477,11 @@ applications that ...

      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. -For each event dispatch, one virtual call is followed by a linear search for -the appropriate reaction, using one RTTI comparison per reaction. The -following alternatives were considered but rejected:

      +active state define exactly which reaction +the state machine will produce. For each event dispatch, one virtual call is +followed by a linear search for the appropriate reaction, using one RTTI +comparison per reaction. The following alternatives were considered but +rejected:

      • Acyclic visitor: This double-dispatch variant satisfies all scalability @@ -479,8 +490,9 @@ following alternatives were considered but rejected:

        slows down construction and makes memory management customization inefficient. In addition, C++ RTTI must inevitably be turned on, with negative effects on executable size. boost::fsm originally employed acyclic - visitor and was about 4 times slower than it is now. The speed might be - better on other platforms but the other negative effects will remain.
      • + visitor and was about 4 times slower than it is now (MSVC7.1 on Intel + Pentium M). The dispatch speed might be better on other platforms but the + other negative effects will remain.
      • GOF Visitor: The GOF Visitor pattern inevitably makes the whole machine @@ -502,7 +514,7 @@ following alternatives were considered but rejected:

        Memory

        On a 32-bit box, one empty state typically needs less than 50 bytes of memory. Even very complex machines will usually have less than 20 -simultaneously current states so just about every machine should run with less +simultaneously active states so just about every machine should run with less than one kilobyte of memory (not counting event queues). Obviously, the per-machine memory footprint is offset by whatever state-local members the user adds.

        @@ -510,29 +522,30 @@ user adds.

        The following ranking should give a rough picture of what feature will consume how many cycles:

          -
        1. state_cast<>: By far the most cycle-consuming feature. +
        2. state_cast<>(): By far the most cycle-consuming feature. Searches linearly for a suitable state, using one dynamic_cast per visited state.
        3. State entry and exit: Profiling of the fully optimized 1-bit-BitMachine suggested that about 100ns of the 210ns total are spent destructing the exited state and constructing the entered state. Obviously, transitions - where the innermost common outer state is "far" from the leaf states and/or - with lots of orthogonal states can easily cause the destruction and - construction of quite a few states leading to significant amounts of time - spent for a transition.
        4. -
        5. state_downcast<>: Searches linearly for the requested + where the innermost + common outer state is "far" from the leaf states and/or with lots of + orthogonal states can easily cause the destruction and construction of quite + a few states leading to significant amounts of time spent for a transition.
        6. +
        7. state_downcast<>(): Searches linearly for the requested state, using one virtual call and one RTTI comparison per visited state.
        8. History: For a state containing a history pseudo state a binary search through the (usually small) history map must be performed on each entry and exit. History slot allocation is performed exactly once, before first entry.
        9. Event dispatch: One virtual call followed by a linear search for a - suitable reaction, using one RTTI comparison per visited reaction.
        10. + suitable reaction, using one RTTI + comparison per visited reaction.
        11. Orthogonal states: One additional virtual call for each exited state - if there is more than one current leaf state before a transition. It + if there is more than one active leaf state before a transition. It should also be noted that the worst-case event dispatch time is multiplied in the presence of orthogonal states. For example, if two orthogonal leaf - states are added to a given current state configuration, the worst-case time - is tripled.
        12. + states are added to a given state configuration, the worst-case time is + tripled.

        Limitations

        Deferring and posting events

        @@ -624,9 +637,9 @@ would consume more resources than the currently implemented limited deep history support. Moreover, full deep history behavior can easily be implemented with shallow history:

        -

        Of course, this only works if D, E or any of their direct or indirect inner -states do not have orthogonal regions. If not so then this pattern has to be -applied recursively.

        +

        Of course, this only works if C, D, E or any of their direct or indirect +inner states do not have orthogonal regions. If not so then this pattern has +to be applied recursively.

        Synchronization (join and fork) bars

        Synchronization bars are not supported, that is, a transition always @@ -636,22 +649,26 @@ emulated with guards. Fork bars are needed only rarely. Their support would complicate the implementation quite a bit.

        Event dispatch to orthogonal regions

        The boost::fsm event dispatch algorithm is different to the one specified -in David Harel's original paper and in the UML standard. Both mandate that -each event is dispatched to all orthogonal regions of a state machine. -Example:

        +in + +David Harel's original paper and in the +UML standard. +Both mandate that each event is dispatched to all orthogonal regions of a +state machine. Example:

        Here the Harel/UML dispatch algorithm specifies that the machine must transition from (B,D) to (C,E) when an EvX event is processed. Because of the -subtleties that Harel describes in chapter 7 of his paper, an implementation -of this algorithm is not only quite complex but also much slower than the -simplified version employed by boost::fsm, which stops searching for reactions -as soon as it has found one suitable for the current event. That is, had the -example been implemented with this library, the machine would have -transitioned non-deterministically from (B,D) to either (C,D) or (B,E). This -version was chosen because, in my experience, in real-world machines different -orthogonal regions often do not specify transitions for the same events. For -the rare cases when they do, the UML behavior can easily be emulated as -follows:

        +subtleties that Harel describes in chapter 7 of + +his paper, an implementation of this algorithm is not only quite complex +but also much slower than the simplified version employed by boost::fsm, which +stops searching for reactions as soon +as it has found one suitable for the current event. That is, had the example +been implemented with this library, the machine would have transitioned +non-deterministically from (B,D) to either (C,D) or (B,E). This version was +chosen because, in my experience, in real-world machines different orthogonal +regions often do not specify transitions for the same events. For the rare +cases when they do, the UML behavior can easily be emulated as follows:

        Transitions across orthogonal regions

        @@ -664,7 +681,7 @@ situation where it would make any sense. If you need to make such transitions, please do let me know!


        Revised -12 October, 2003

        +01 December, 2003

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

        diff --git a/doc/rationale.pdf b/doc/rationale.pdf index f3b021f..6f6c23b 100644 Binary files a/doc/rationale.pdf and b/doc/rationale.pdf differ diff --git a/doc/reference.html b/doc/reference.html new file mode 100644 index 0000000..cb59649 --- /dev/null +++ b/doc/reference.html @@ -0,0 +1,927 @@ + + + + + + + + +The boost::fsm library - Tutorial + + + + +
      Machine configuration
      - # states / # outgoing transitions per state
      Event dispatch & transition time [nanoseconds]Machine configuration
      + # states / # outgoing transitions per state
      Event dispatch & transition time + [nanoseconds]
      Out of the box Same as out of the box but with - BOOST_FSM_USE_NATIVE_RTTI defined Same as out of the box but with customized memory management
      + + + + +
      +

      + C++ Boost

      +
      +

      The boost::fsm library

      +

      Reference

      +
      +
      +

      Contents

      +
      +
      Concepts
      +
      ExceptionTranslator
      +
      StateBase
      +
      Worker
      +
      state_machine.hpp
      +
      Class template + state_machine
      +
      asynchronous_state_machine.hpp
      +
      Class template + asynchronous_state_machine
      +
      simple_state.hpp
      +
      Typedef no_reactions
      +
      Enum history_mode
      +
      Class template simple_state
      +
      state.hpp
      +
      Class template state
      +
      +
      +

      Concepts

      +

      ExceptionTranslator concept

      +

      An ExceptionTranslator type defines how C++ exceptions occurring during +state machine operation are translated to exception events. Every model of +this concept must provide an operator() with the following +signature:

      +
      template< class Action, class ExceptionEventHandler >
      +result operator()(
      +  Action action,
      +  ExceptionEventHandler eventHandler,
      +  result handlerSuccessResult );
      +

      For an ExceptionTranslator object e the following expression +must be well-formed and have the indicated results:

      + + + + + + + + + + + +
      ExpressionTypeEffects/Result
      result action();
      + bool exceptEventHandler(
      +  const event_base & );
      + result
      +  handlerSuccessResult;
      +
      + e(
      +  &action,
      +  &exceptEventHandler,
      +  handlerSuccessResult );
      result +
        +
      1. Attempts to execute action and to return the result
      2. +
      3. Catches the exception propagated from action, if + necessary
      4. +
      5. Translates the exception to a suitable event subclass + and constructs an object of the event
      6. +
      7. Passes the event object to exceptEventHandler
      8. +
      9. Rethrows the original exception if exceptEventHandler + returns false, returns handlerSuccessResult + otherwise
      10. +
      +
      +

      StateBase concept

      +

      A StateBase type is the common base of all states of a given state machine +type. state_machine<>::state_base_type is a model of the +StateBase concept.

      +

      For a StateBase type S and a const object +cs of that type the following expressions must be well-formed and have +the indicated results:

      + + + + + + + + + + + + + + + + + + + + + +
      ExpressionTypeResult
      cs.outer_state_ptr()const S *0 if cs is an + outermost state, a pointer + to the outer state of cs otherwise
      cs.dynamic_type()S::id_typeA value unambiguously identifying the most-derived type of + cs. S::id_type values are comparable with + operator== and operator!=. An unspecified collating + order can be established with std::less< S::id_type >
      cs.custom_dynamic_type_ptr<
      +  Type >()
      const Type *A pointer to the custom type identifier or 0. + If != 0, Type must match the type of the + previously set pointer. The result is undefined if this is not the case.
      +

      Worker concept

      +

      todo

      +

      Header <boost/fsm/state_machine.hpp>

      +

      Class template state_machine

      +

      This is the base class template of all synchronous state machines.

      +

      Class template state_machine parameters

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Template parameterRequirementsSemanticsDefault
      MostDerivedThe most-derived subclass of this class template  
      InitialStateA most-derived direct or indirect subclass of either the + simple_state or the state class template. The + type that this class passes as Context to its base class + template must be equal to MostDerived. That is, + InitialState must be an + outermost state of this state machineThe state that is entered when state_machine<>::
      + initiate()
      is called
       
      AllocatorA model of the standard Allocator concept std::allocator< void >
      ExceptionTranslatorA model of the ExceptionTranslator conceptsee ExceptionTranslator + concept exception_translator<>
      +

      Class template state_machine synopsis

      +
      namespace boost
      +{
      +namespace fsm
      +{
      +  template<
      +    class MostDerived,
      +    class InitialState,
      +    class Allocator = std::allocator< void >,
      +    class ExceptionTranslator = exception_translator<> >
      +  class state_machine : noncopyable
      +  {
      +    public:
      +      typedef MostDerived outermost_context_type;
      +
      +      bool initiate();
      +      void terminate();
      +      bool terminated() const;
      +
      +      bool process_event( const event_base & );
      +
      +      template< class Target >
      +      Target state_cast() const;
      +      template< class Target >
      +      Target state_downcast() const;
      +
      +      // a model of the StateBase concept
      +      typedef implementation-defined state_base_type;
      +      // a model of the standard Forward Iterator concept
      +      typedef implementation-defined state_iterator;
      +
      +      state_iterator state_begin() const;
      +      state_iterator state_end() const;
      +
      +    protected:
      +      state_machine();
      +      ~state_machine();
      +  };
      +}
      +}
      +

      Class template state_machine constructor and destructor

      +
      state_machine();
      +

      Effects: Constructs a non-running state machine
      +Postcondition: terminated()

      +
      ~state_machine();
      +

      Effects: terminate();

      +

      Class template state_machine modifier functions

      +
      bool initiate();
      +

      Effects:

      +
        +
      1. Calls terminate()
      2. +
      3. Constructs a function object action with a parameter-less + operator() returning result that
          +
        1. enters (constructs) the state specified with the InitialState + template parameter
        2. +
        3. enters the tree formed by the direct and indirect inner initial states + of InitialState depth first
        4. +
        +
      4. +
      5. Constructs a function object exceptionEventHandler with an + operator() returning bool and accepting an + exception event parameter that processes the passed exception event, with + the following differences to the processing of normal events:
          +
        • Reaction search always starts + with the outermost unstable + state
        • +
        • As for normal events, reaction search moves outward when the current + state cannot handle the event. However, if there is no outer state (an + outermost state has been + reached) the reaction search is considered unsuccessful. That is, + exception events will never be dispatched to orthogonal regions other than + the one that caused the exception event
        • +
        • Should an exception be thrown during exception event reaction search + or reaction execution then the exception is propagated out of the + exceptionEventHandler function object (that is, + ExceptionTranslator is not used to process exception events)
        • +
        • If no reaction could be found for the exception event or if the state + machine is not stable after processing the exception event, false + is returned from the exceptionEventHandler function object. + Otherwise, true is returned
        • +
        +
      6. +
      7. Passes action, exceptionEventHandler and the + result value handlerSuccessResult to + ExceptionTranslator::operator(). If + ExceptionTranslator::operator() throws an exception, + terminate() is called and the exception is propagated to the caller. + Continues with step 5 otherwise (the return value is discarded)
      8. +
      9. Processes all posted events (see process_event)
      10. +
      +

      Returns: terminated()
      +Throws: Any exceptions propagated from +ExceptionTranslator::operator(). Exceptions never originate in the +library itself but only in code supplied through template parameters. That is, +std::bad_alloc thrown by Allocator::allocate as well +as any exceptions thrown by user-supplied react functions, +transition-actions and entry-actions

      +
      void terminate();
      +

      Effects: The state machine exits (destructs) all currently active +states. Innermost states are +exited first. Other states are exited as soon as all their direct and indirect +inner states have been exited
      +Postcondition: terminated()

      +
      bool process_event( const event_base & );
      +

      Effects:

      +
        +
      1. Selects the passed event as the current event
      2. +
      3. Starts a new reaction search
      4. +
      5. Selects an arbitrary but in this reaction search not yet visited state + from all the currently active + innermost states. If no such state exists then continues with step 10
      6. +
      7. Constructs a function object action with a parameter-less + operator() returning result that does the + following:
          +
        1. Searches a reaction suitable for the current event, starting with the + current innermost state and moving outward until a state defining a + reaction for the event is found. Returns simple_state::forward_event() + if no reaction has been found.
        2. +
        3. Executes the found reaction. If the reaction result is equal to the + return value of simple_state::forward_event() then resumes + the reaction search (step a). Returns the reaction result otherwise
        4. +
        +
      8. +
      9. Constructs a function object exceptionEventHandler with an + operator() accepting an exception event parameter and returning + bool that processes the passed exception event, with the + following differences to the processing of normal events:
          +
        • If the state machine is stable when the exception event is processed + then exception event reaction search starts with the innermost state that + was last visited during the last normal event reaction search (the + exception event was generated as a result of this normal reaction search)
        • +
        • If the state machine is + unstable when the + exception event is processed then exception event reaction search starts + with the outermost unstable + state
        • +
        • As for normal events, reaction search moves outward when the current + state cannot handle the event. However, if there is no outer state (an + outermost state has been + reached) the reaction search is considered unsuccessful. That is, + exception events will never be dispatched to orthogonal regions other than + the one that caused the exception event
        • +
        • Should an exception be thrown during exception event reaction search + or reaction execution then the exception is propagated out of the + exceptionEventHandler function object (that is, + ExceptionTranslator is not used to process exception events)
        • +
        • If no reaction could be found for the exception event or if the state + machine is not stable after processing the exception event, false + is returned from the exceptionEventHandler function object. + Otherwise, true is returned
        • +
        +
      10. +
      11. Passes action, an exceptionEventHandler + callback and the fsm::result value handlerSuccessResult + to ExceptionTranslator::operator(). If + ExceptionTranslator::operator() throws an exception then calls + terminate() and propagates the exception to the caller
      12. +
      13. If the return value of ExceptionTranslator::operator() is + equal to the one of simple_state::forward_event() then + continues with step 3
      14. +
      15. If the return value of ExceptionTranslator::operator() is + equal to the one of simple_state::defer_event() then the + current event is stored in a state-specific queue. Continues with step 10
      16. +
      17. If ExceptionTranslator::operator() returns the previously + passed handlerSuccessResult or if the return value is equal to + the one of simple_state::discard_event() then continues with + step 10
      18. +
      19. If the posted events queue is non-empty then dequeues the first event, + selects it as the current event and continues with step 2. Returns to the + caller otherwise
      20. +
      +

      Returns: false, if the machine was terminated before +processing the event. Returns terminated() otherwise
      +Throws: Any exceptions propagated from +ExceptionTranslator::operator(). Exceptions never originate in the +library itself but only in code supplied through template parameters. That is, +std::bad_alloc thrown by Allocator::allocate as well +as any exceptions thrown by user-supplied reactions, transition-actions and +entry-actions

      +

      Class template state_machine observer functions

      +
      bool terminated() const;
      +

      Returns: true, if the machine is terminated. Returns +false otherwise
      +Note: Is equivalent to state_begin() == state_end()

      +
      template< class Target >
      +Target state_cast() const;
      +

      Returns: Depending on the form of Target either a +reference or a pointer to const if at least one of the currently +active states can successfully be dynamic_cast to Target. +Returns 0 for pointer targets and throws std::bad_cast +for reference targets otherwise. Target can take either of the +following forms: const Class * or const Class &
      +Throws: std::bad_cast if Target is a +reference type and none of the active states can be dynamic_cast +to Target
      +Note: The search sequence is the same as for event dispatch

      +
      template< class Target >
      +Target state_downcast() const;
      +

      Returns: Depending on the form of Target either a +reference or a pointer to const if Target is equal +to the most-derived type of a currently active state. Returns 0 +for pointer targets and throws std::bad_cast for reference +targets otherwise. Target can take either of the following forms: +const Class * or const Class &
      +Throws: std::bad_cast if Target is a +reference type and none of the active states has a most derived type equal to +Target
      +Note: The search sequence is the same as for event dispatch

      +
      state_iterator state_begin() const;
      +
      state_iterator state_end() const;
      +

      Return: Iterator objects, the range [state_begin(), +state_end()) refers to all currently active +innermost states. For an object +i of type state_iterator, *i returns a +const state_base_type & and i.operator->() returns a +const state_base_type *
      +Note: The position of individual innermost states in the range is +undefined. Their position may change with each call to a modifier function. +Moreover, all iterators are invalidated when a modifier function is called

      +

      Header <boost/fsm/asynchronous_state_machine.hpp>

      +

      Class template +asynchronous_state_machine

      +

      This is the base class template of all asynchronous state machines.

      +

      Class template asynchronous_state_machine parameters

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Template parameterRequirementsSemanticsDefault
      MostDerivedThe most-derived subclass of this class template  
      InitialStateA most-derived direct or indirect subclass of either the + simple_state or the state class template. The + type that this class passes as Context to its base class + template must be equal to MostDerived. That is, + InitialState must be an + outermost state of this state machineThe state that is entered when Worker::
      + operator()()
      is called
       
      WorkerA model of the Worker conceptsee Worker concept worker<>
      AllocatorA model of the standard Allocator concept std::allocator< void >
      ExceptionTranslatorA model of the ExceptionTranslator conceptsee ExceptionTranslator + concept exception_translator<>
      +

      Class template asynchronous_state_machine synopsis

      +
      template<
      +  class MostDerived,
      +  class InitialState,
      +  class Worker = worker<>,
      +  class Allocator = std::allocator< void >,
      +  class ExceptionTranslator = exception_translator<> >
      +class asynchronous_state_machine : implementation-defined
      +{
      +  public:
      +    void queue_event( const intrusive_ptr< event_base > & );
      +
      +  protected:
      +    asynchronous_state_machine( Worker & myWorker );
      +    ~asynchronous_state_machine();  
      +};
      +

      Class template asynchronous_state_machine constructor and +destructor

      +
      asynchronous_state_machine( Worker & myWorker );
      +

      Precondition: No thread of control is currently inside +myWorker.operator()
      +Effects
      : Constructs a non-running asynchronous state machine and registers +it with the passed worker
      +Throws: Whatever Allocator::allocate (invoked by the +worker) throws

      +
      ~asynchronous_state_machine();
      +

      Precondition: No thread of control is currently inside +myWorker.operator(). The worker object passed to the constructor has +not yet been destructed
      +Effects
      : Terminates the state machine

      +

      Class template asynchronous_state_machine modifier functions

      +
      void queue_event( const intrusive_ptr< event_base > & );
      +

      Effects: Pushes the passed event into the queue of the worker object +passed to the constructor
      +Throws: Whatever Allocator::allocate (invoked by the +worker) throws

      +

      Header <boost/fsm/simple_state.hpp>

      +

      Typedef no_reactions

      +

      This is the default value for the Reactions parameter of the +simple_state class template. Necessary for the rare cases when a +state without reactions has inner states.

      +
      namespace boost
      +{
      +namespace fsm
      +{
      +  typedef implementation-defined no_reactions;
      +}
      +}
      +

      Enum history_mode

      +

      Defines the history type of a state.

      +
      namespace boost
      +{
      +namespace fsm
      +{
      +  enum history_mode
      +  {
      +    has_no_history,
      +    has_shallow_history,
      +    has_deep_history,
      +    has_full_history // shallow & deep
      +  };
      +}
      +}
      +

      Class template simple_state

      +

      The base class template of all states that do not need to call any +of the following simple_state member functions from their +constructors:

      +
      void post_event(
      +  const intrusive_ptr< const event_base > & );
      +
      +outermost_context_type & outermost_context();
      +const outermost_context_type & outermost_context() const;
      +
      +template< class OtherContext >
      +OtherContext & context();
      +template< class OtherContext >
      +const OtherContext & context() const;
      +
      +template< class Target >
      +Target state_cast() const;
      +template< class Target >
      +Target state_downcast() const;
      +

      States that need to call any of these functions from their constructors +must derive from the state class template.

      +

      Class template simple_state parameters

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Template parameterRequirementsSemanticsDefault
      MostDerivedThe most-derived subclass of this class template  
      ContextA most-derived direct or indirect subclass of either the + state_machine, asynchronous_state_machine, + simple_state or state class templates or an + instantiation of the orthogonal class template nested in the + state base classes. Must be a complete typeDefines the states' position in the state hierarchy 
      ReactionsAn mpl::list containing instantiations of the + custom_reaction, deferral, termination + or transition class templates. If there is only a single + reaction then it can also be passed directly, without wrapping it into an + mpl::listDefines to which events a state can reactno_reactions
      InnerInitialAn mpl::list containing most-derived direct + or indirect subclasses of either the simple_state or the + state class template or instantiations of either the + shallow_history or deep_history class templates. If + there is only a single non-history inner initial state then it can also be + passed directly, without wrapping it into an mpl::list. The + type that each state in the list passes as Context to its + base class template must correspond to the orthogonal region it belongs + to. That is, the first state in the list must pass + MostDerived::orthogonal< 0 >, the second + MostDerived::orthogonal< 1 > and so forth. + MostDerived::orthogonal< 0 > and MostDerived are + synonymousDefines the inner initial state for each orthogonal + region. By default, a state does not have inner statesunspecified
      historyModeOne of the values defined in the history_mode + enumerationDefines whether the state saves shallow, deep or both + histories upon exithas_no_history
      +

      Class template simple_state synopsis

      +
      namespace boost
      +{
      +namespace fsm
      +{
      +  template<
      +    class MostDerived,
      +    class Context,
      +    class Reactions = no_reactions,
      +    class InnerInitial = unspecified,
      +    history_mode historyMode = has_no_history >
      +  class simple_state : implementation-defined
      +  {
      +    public:
      +      // see template parameters
      +      template< implementation-defined-unsigned-integer-type
      +        innerOrthogonalPosition >
      +      struct orthogonal
      +      {
      +        // implementation-defined
      +      };
      +
      +      typedef typename Context::outermost_context_type
      +        outermost_context_type;
      +
      +      outermost_context_type & outermost_context();
      +      const outermost_context_type & outermost_context() const;
      +
      +      template< class OtherContext >
      +      OtherContext & context();
      +      template< class OtherContext >
      +      const OtherContext & context() const;
      +
      +      template< class Target >
      +      Target state_cast() const;
      +      template< class Target >
      +      Target state_downcast() const;
      +
      +      void post_event(
      +        const intrusive_ptr< const event_base > & );
      +
      +      result discard_event();
      +      result forward_event();
      +      result defer_event();
      +      template< class DestinationState >
      +      result transit();
      +      template<
      +        class DestinationState,
      +        class TransitionContext,
      +        class Event >
      +      result transit(
      +        void ( TransitionContext::* )( const Event & ),
      +        const Event & );
      +      result terminate();
      +
      +      static id_type static_type();
      +
      +      template< class CustomId >
      +      static const CustomId * custom_static_type_ptr();
      +
      +      template< class CustomId >
      +      static void custom_static_type_ptr( const CustomId * );
      +
      +    protected:
      +      simple_state();
      +      virtual ~simple_state();
      +  };
      +}
      +}
      +

      Class template simple_state constructor and destructor

      +
      simple_state();
      +

      Effects: Depending on the historyMode parameter, +reserves storage to store none, shallow, deep or both histories
      +Throws: Any exceptions propagated from Allocator::allocate +(the template parameter passed to the base class of +outermost_context_type)
      +Note: The constructors of all direct and indirect subclasses should be +exception-neutral

      +
      virtual ~simple_state();
      +

      Effects: Depending on the historyMode parameter, stores +none, shallow, deep or both histories. Pushes all events deferred by the state +into the posted events queue

      +

      Class template simple_state modifier functions

      +
      void post_event(
      +  const intrusive_ptr< const event_base > & );
      +

      Effects: Pushes the passed event into the state machine's posted +events queue
      +Throws: Any exceptions propagated from Allocator::allocate +(the template parameter passed to the base class of +outermost_context_type)
      +Note: Unless the direct subclass is the state class +template, this function must not be called from the constructors of direct and +indirect subclasses. All direct and indirect callers should be +exception-neutral

      +
      result discard_event();
      +

      Effects: Instructs the state machine to discard the current event +and to continue with the processing of the remaining events (see +state_machine::process_event for details)
      +Returns: An unspecified value of the result enumeration. +The user-supplied react member function must return this value to +its caller
      +Note: Must only be called from within react member +functions, which are called by custom_reaction instantiations. +All direct and indirect callers should be exception-neutral

      +
      result forward_event();
      +

      Effects: Instructs the state machine to forward the current event to +the next state (see +state_machine::process_event for details)
      +Returns: An unspecified value of the result enumeration. +The user-supplied react member function must return this value to +its caller
      +Note: Must only be called from within react member +functions, which are called by custom_reaction instantiations. +All direct and indirect callers should be exception-neutral

      +
      result defer_event();
      +

      Effects: Instructs the state machine to defer the current event and +to continue with the processing of the remaining events (see +state_machine::process_event for details)
      +Returns: An unspecified value of the result enumeration. +The user-supplied react member function must return this value to +its caller
      +Note: Must only be called from within react member +functions, which are called by custom_reaction instantiations. +All direct and indirect callers should be exception-neutral

      +
      template< class DestinationState >
      +result transit();
      +

      Effects:

      +
        +
      1. Exits (destructs) all currently active direct and indirect inner states + of the innermost common outer state of this state and DestinationState. + Innermost states are exited first. Other states are exited as soon as all + their direct and indirect inner states have been exited
      2. +
      3. Enters (constructs) the state that is both a direct inner state of the + innermost common outer state and either the DestinationState + itself or a direct or indirect outer state of DestinationState +
      4. +
      5. Enters (constructs) the tree formed by the direct and indirect inner + states of the previously entered state down to the DestinationState + depth first
      6. +
      7. Enters (constructs) the tree formed by the direct and indirect inner + initial states of DestinationState depth first
      8. +
      9. Instructs the state machine to discard the current event and to continue + with the processing of the remaining events (see + state_machine::process_event for + details)
      10. +
      +

      Returns: An unspecified value of the result +enumeration. The user-supplied react member function must return +this value to its caller
      +Throws: Any exceptions propagated from operator new (used +to allocate states), state constructors or Allocator::allocate +(the template parameter passed to the base class of +outermost_context_type)
      +Note: Must only be called from within react member +functions, which are called by custom_reaction instantiations. +All direct and indirect callers should be exception-neutral
      +Caution: Inevitably exits (destructs) this state before returning to +the calling react member function, which must therefore not +attempt to access anything except stack objects before returning to its caller

      +
      template<
      +  class DestinationState,
      +  class TransitionContext,
      +  class Event >
      +result transit(
      +  void ( TransitionContext::* )( const Event & ),
      +  const Event & );
      +

      Effects:

      +
        +
      1. Exits (destructs) all currently active direct and indirect inner states + of the innermost common outer state of this state and DestinationState. + Innermost states are exited first. Other states are exited as soon as all + their direct and indirect inner states have been exited
      2. +
      3. Executes the passed transition action, forwarding the passed event
      4. +
      5. Enters (constructs) the state that is both a direct inner state of the + innermost common outer state and either the DestinationState + itself or a direct or indirect outer state of DestinationState +
      6. +
      7. Enters (constructs) the tree formed by the direct and indirect inner + states of the previously entered state down to the DestinationState + depth first
      8. +
      9. Enters (constructs) the tree formed by the direct and indirect inner + initial states of DestinationState depth first
      10. +
      11. Instructs the state machine to discard the current event and to continue + with the processing of the remaining events (see + state_machine::process_event for + details)
      12. +
      +

      Returns: An unspecified value of the result +enumeration. The user-supplied react member function must return +this value to its caller
      +Throws: Any exceptions propagated from operator new (used +to allocate states), state constructors, the transition action or +Allocator::allocate (the template parameter passed to the base class of +outermost_context_type)
      +Note: Must only be called from within react member +functions, which are called by custom_reaction instantiations. +All direct and indirect callers should be exception-neutral
      +Caution: Inevitably exits (destructs) this state before returning to +the calling react member function, which must therefore not +attempt to access anything except stack objects before returning to its caller

      +
      result terminate();
      +

      Effects: Terminates the state and instructs the state machine to +discard the current event and to continue with the processing of the remaining +events (see state_machine::process_event +for details)
      +Returns: An unspecified value of the result enumeration. +The user-supplied react member function must return this value to +its caller
      +Note: Must only be called from within react member +functions, which are called by custom_reaction instantiations. +All direct and indirect callers should be exception-neutral
      +Caution: Inevitably exits (destructs) this state before returning to +the calling react member function, which must therefore not +attempt to access anything except stack objects before returning to its caller

      +

      Class template simple_state observer functions

      +
      outermost_context_type & outermost_context();
      +

      Returns: A reference to the outermost context, which is always the +state machine this state belongs to
      +Note: Unless the direct subclass is the state class +template, this function must not be called from the constructors of direct and +indirect subclasses

      +
      const outermost_context_type & outermost_context() const;
      +

      Returns: A reference to the const outermost context, which is always +the state machine this state belongs to
      +Note: Unless the direct subclass is the state class +template, this function must not be called from the constructors of direct and +indirect subclasses

      +
      template< class OtherContext >
      +OtherContext & context();
      +

      Returns: A reference to a direct or indirect context
      +Note: Unless the direct subclass is the state class +template, this function must not be called from the constructors of direct and +indirect subclasses

      +
      template< class OtherContext >
      +const OtherContext & context() const;
      +

      Returns: A reference to a const direct or indirect context
      +Note: Unless the direct subclass is the state class +template, this function must not be called from the constructors of direct and +indirect subclasses

      +
      template< class Target >
      +Target state_cast() const;
      +

      Returns: Has exactly the same semantics as +state_machine::state_cast
      +Note: Unless the direct subclass is the state class +template, this function must not be called from the constructors of direct and +indirect subclasses. The result is unspecified if this function is +called when the machine is not stable

      +
      template< class Target >
      +Target state_downcast() const;
      +

      Returns: Has exactly the same semantics as +state_machine::state_downcast
      +Note: Unless the direct subclass is the state class +template, this function must not be called from the constructors of direct and +indirect subclasses. The result is unspecified if this function is +called when the machine is not stable

      +

      Class template simple_state static functions

      +
      static id_type static_type();
      +

      Returns: A value unambiguously identifying the type of +MostDerived. id_type values are comparable with +operator== and operator!=. An unspecified collating order +can be established with std::less< S::id_type >

      +
      template< class CustomId >
      +static const CustomId * custom_static_type_ptr();
      +

      Returns: The pointer to the custom type identifier for +MostDerived or 0. If != 0, CustomId must +match the type of the previously set pointer. The result is undefined if this +is not the case
      +Note: This function is not available if + +BOOST_FSM_USE_NATIVE_RTTI is defined

      +
      template< class CustomId >
      +static void custom_static_type_ptr( const CustomId * );
      +

      Effects: Sets the pointer to the custom type identifier for +MostDerived
      +Note: This function is not available if + +BOOST_FSM_USE_NATIVE_RTTI is defined

      +

      Header <boost/fsm/state.hpp>

      +

      Class template state

      +

      This is the base class template of all states that need to call any of the +following simple_state member functions from their constructors:

      +
      void post_event(
      +  const intrusive_ptr< const event_base > & );
      +
      +outermost_context_type & outermost_context();
      +const outermost_context_type & outermost_context() const;
      +
      +template< class OtherContext >
      +OtherContext & context();
      +template< class OtherContext >
      +const OtherContext & context() const;
      +
      +template< class Target >
      +Target state_cast() const;
      +template< class Target >
      +Target state_downcast() const;
      +

      States that do not need to call any of these functions from their +constructors should rather derive from the simple_state class +template, what saves the implementation of the forwarding constructor.

      +

      Class template state synopsis

      +
      namespace boost
      +{
      +namespace fsm
      +{
      +  template<
      +    class MostDerived,
      +    class Context,
      +    class Reactions = no_reactions,
      +    class InnerInitial = unspecified,
      +    history_mode historyMode = has_no_history >
      +  class state : public simple_state<
      +    MostDerived, Context, Reactions, InnerInitial, historyMode >
      +  {
      +    protected:
      +      struct my_context
      +      {
      +        // implementation-defined
      +      };
      +
      +      typedef state my_base;
      +
      +      state( my_context ctx );
      +      virtual ~state();
      +  };
      +}
      +}
      +

      Direct and indirect subclasses of state must provide a +constructor with the same signature as the state constructor, +forwarding the context parameter.

      +
      +

      Revised +12 December, 2003

      +

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

      + + + + diff --git a/doc/reference.pdf b/doc/reference.pdf new file mode 100644 index 0000000..3793ed4 Binary files /dev/null and b/doc/reference.pdf differ diff --git a/doc/tutorial.html b/doc/tutorial.html index f31b861..d6e415f 100644 --- a/doc/tutorial.html +++ b/doc/tutorial.html @@ -24,9 +24,14 @@
      +

      The Japanese translation of this tutorial can be found at + +http://prdownloads.sourceforge.jp/jyugem/7127/fsm-tutorial-jp.pdf. Kindly +contributed by Mitsuo Fukasawa.

      Contents

      Introduction
      +
      How to read this tutorial
      Hello World!
      A stop watch
      Defining states and events
      @@ -38,8 +43,7 @@
      Spreading a state machine over multiple translation units
      Guards
      -
      In-state reactions - (aka inner transitions)
      +
      In-state reactions
      Transition actions
      Advanced topics
      Reaction function reference
      @@ -50,6 +54,8 @@
      Deferring events
      History
      Orthogonal states
      +
      State queries
      +
      State type information
      Exception handling
      Submachines & Parametrized States
      @@ -72,6 +78,25 @@ The UML specifications can be found at http://www.omg.org/cgi-bin/doc?formal/03-03-01 (see chapters 2.12 and 3.74).

      All examples have been tested with MSVC7.1 and boost distribution 1.30.2.

      +

      How to read this tutorial

      +

      This tutorial was designed to be read linearly. First time users should +start reading right at the beginning and stop as soon as they know enough for +the task at hand. Specifically:

      +
        +
      • The tutorial starts out with the Hello World! + and stop watch examples explaining the most + basic features that all users of the library should understand. Small and + simple machines with just a handful of states can be implemented reasonably + well by using just these features.
      • +
      • Afterwards, the digital camera example + explains the intermediate features most of which should be known by anyone + wanting to build larger machines with up to roughly a dozen states.
      • +
      • Finally, users wanting to create even more complex machines and project + architects evaluating boost::fsm should also read the + Advanced topics section at the end. Moreover, + reading the Limitations section in + the Rationale is strongly suggested.
      • +

      Hello World!

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

      @@ -109,9 +134,15 @@ is destroyed what automatically exits the Greeting state.

      class template.
    7. The machine is not yet running after construction. We start it by calling initiate().
    8. -
    9. All states reside in a context. For the moment, this context is the - state machine. That's why Machine is passed as the second - template parameter of Greeting's base.
    10. +
    11. Whenever the state machine enters a state it creates an object of the + corresponding state class. The object is then kept alive as long as the + machine remains in the state. Finally, the object is destroyed when the + state machine exits the state. Therefore, a state entry action can be + defined by adding a constructor and a state exit action can be defined by + adding a destructor.
    12. +
    13. All states reside in a context. + For the moment, this context is the state machine. That's why Machine + is passed as the second template parameter of Greeting's base.
    14. The state machine must be informed which state it has to enter when the machine is initiated. That's why Greeting is passed as the second template parameter of Machine's base. We have to forward @@ -182,15 +213,16 @@ int main()
    15. The simple_state class template accepts up to four parameters.
        -
      • The third parameter specifies reactions (explained in due course). - Because there aren't any yet, we pass fsm::no_reactions, - which is also the default.
      • +
      • The third parameter specifies + reactions (explained in due course). Because there aren't any yet, we + pass fsm::no_reactions, which is also the default.
      • The fourth parameter specifies the inner initial state, if there is one.
    16. A state is defined as an inner state simply by passing its outer state - as its context (where outermost states pass the state machine).
    17. + as its context (where outermost + states pass the state machine).
    18. Because the context of a state must be a complete type (i.e. not forward declared), a machine must be defined from "outside to inside". That is, we always start with the state machine, followed by outermost states, followed @@ -203,9 +235,8 @@ int main() declarations.
    19. Adding reactions

      -

      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:

      +

      For the moment we will use only one type of reaction: transitions. We +insert the bold parts of the following code:

      #include <boost/fsm/transition.hpp>
       
       // ...
      @@ -229,7 +260,7 @@ int main()
         return 0;
       }

      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 +put them into an mpl::list as soon as there is more than one of them (see Specifying multiple reactions for a state).
      Now we have all the states and all the transitions in place and a number of @@ -397,7 +428,8 @@ limitations:

      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. -
    20. There is no way to specify in-state reactions (aka inner transitions).
    21. +
    22. There is no way to specify + in-state reactions.
    23. All these limitations can be overcome with custom reactions. Warning: It is easy to abuse custom reactions up to the point of invoking undefined @@ -450,8 +482,8 @@ struct EvConfig : fsm::event< EvConfig > {}; struct NotShooting; struct Camera : fsm::state_machine< Camera, NotShooting > { - bool IsMemoryAvailable() const { return true; } - bool IsBatteryLow() const { return false; } + bool IsMemoryAvailable() const { return true; } + bool IsBatteryLow() const { return false; } }; struct Idle; @@ -494,10 +526,10 @@ fsm::result Idle::react( const EvConfig & )

      Caution: Any call to the simple_state::transit<>() or simple_state::terminate() (see Reaction function reference) -member functions will inevitably destruct the current state object (similar to -delete this;)! That is, code executed after any of these calls -may invoke undefined behavior! That's why these functions should -only be called as part of a return statement.

      +member functions will inevitably destruct the state object (similar to +delete this;)! That is, code executed after any of these calls may +invoke undefined behavior! That's why these functions should only +be called as part of a return statement.

      Guards

      The inner workings of the Shooting state could look as follows:

      @@ -560,11 +592,10 @@ fsm::result NotShooting::react( const EvShutterHalf & ) } } // ...
    -

    In-state reactions (aka -inner transitions)

    -

    The self-transition of the Focused state could also be implemented as an -in-state reaction, which has the same effect as long as Focused does not have -any entry or exit actions:

    +

    In-state reactions

    +

    The self-transition of the Focused state could also be implemented as an +in-state reaction, which has +the same effect as long as Focused does not have any entry or exit actions:

    Shooting.cpp:

    // ...
     fsm::result Focused::react( const EvShutterFull & )
    @@ -577,7 +608,8 @@ fsm::result Focused::react( const EvShutterFull & )
       {
         std::cout << "Cache memory full. Please wait...\n";
         // Indicate that the event can be discarded. So, the 
    -    // dispatch algorithm will stop looking for a reaction.
    +    // dispatch algorithm will stop looking for a reaction
    +    // and the machine remains in the Focused state.
         return discard_event();
       }
     }
    @@ -586,8 +618,9 @@ fsm::result Focused::react( const EvShutterFull & )
     

    As an effect of every transition, actions are executed in the following order:

      -
    1. Starting from the innermost current state, all exit actions up to but - excluding the innermost common outer state (aka LCA, least common ancestor).
    2. +
    3. Starting from the innermost active state, all exit actions up to but + excluding the + innermost common outer state.
    4. The transition action (if present).
    5. Starting from the innermost common outer state, all entry actions down to the target state followed by the entry actions of the initial states.
    6. @@ -653,7 +686,7 @@ member functions, which must return by calling exactly one function define a reaction for the event.
    7. simple_state::discard_event(): The dispatch algorithm stops searching for a reaction and the current event is discarded. Useful to - implement in-state reactions.
    8. + implement in-state reactions.
    9. simple_state::defer_event(): The current event is pushed into a separate queue and the dispatch algorithm stops searching for a reaction. When the state is exited later, the separate queue is emptied into @@ -717,7 +750,7 @@ example of how to do this:

      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 +entry actions is a bit more complicated (see e.g. Focusing::Focusing() in Shooting.cpp in the Camera example):

      struct Pumping : fsm::state< Pumping, Purifier >
       {
      @@ -746,7 +779,7 @@ so this workaround should not uglify user code too much.

      Deferring events

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

      struct Event : fsm::event< Event > {};
       struct Initial;
      @@ -782,8 +815,9 @@ chart as follows:

      As mentioned earlier, the Configuring state contains a fairly complex and deeply nested inner machine. Naturally, we'd like to restore the previous -state down to the innermost state(s) in Configuring, that's why we use a deep -history pseudo state. The associated code looks as follows:

      +state down to the innermost state(s) +in Configuring, that's why we use a deep history pseudo state. The associated +code looks as follows:

      // not part of the Camera example
       struct NotShooting : fsm::simple_state< NotShooting, Camera,
         /* ... */, Idle, fsm::has_deep_history > //
      @@ -809,11 +843,11 @@ and the appropriate states entered. The former is expressed by passing either
       fsm::has_full_history (which combines shallow and deep history) 
       as the last parameter to the simple_state and state 
       templates. The latter is expressed by specifying either 
      -fsm::shallow_history<> or fsm::deep_history<> as a 
      -transition destination or, as we'll see in an instant, as an inner initial 
      -state. Because it is possible that a state containing a history pseudo state 
      -has never been entered before a transition to history is made, both templates 
      -demand a parameter specifying the default state to enter in such situations.

      +fsm::shallow_history or fsm::deep_history as a transition +destination or, as we'll see in an instant, as an inner initial state. Because +it is possible that a state containing a history pseudo state has never been +entered before a transition to history is made, both class templates demand a +parameter specifying the default state to enter in such situations.

      The redundancy necessary for using history is checked for consistency at compile time. That is, the state machine wouldn't have compiled had we forgotten to pass fsm::has_deep_history to the base of @@ -835,10 +869,10 @@ struct NotShooting : fsm::simple_state< NotShooting, Camera, // ...

      Unfortunately, there is a small inconvenience due to some template-related implementation details. When the inner initial state is a class template -instantiation we always have to put it into an mpl::list<>, +instantiation we always have to put it into an mpl::list, although there is only one inner initial state. Moreover, the current deep -history implementation has some limitations. Please have a look at the -Limitations chapter in the Rationale.

      +history implementation has some +limitations.

      Orthogonal states

      To implement this state chart you simply specify more than one inner @@ -904,17 +938,19 @@ struct CapsLockOn : fsm::simple_state< struct CapsLockOff : fsm::simple_state< CapsLockOff, Active::orthogonal< 2 >, fsm::transition< EvCapsLockPressed, CapsLockOn > > {};

      -

      State queries

      -

      Often reactions in a state machine depend on the current state in one or +

      State queries

      +

      Often reactions in a state machine depend on the active state in one or more orthogonal regions. This is because orthogonal regions are not completely orthogonal or a certain reaction in an outer state can only take place if the -inner orthogonal regions are in particular states. For this purpose, the -previously introduced state_cast<>() function is also available -within states.

      -

      As a somewhat far-fetched example, let's assume that our keyboard above -also accepts EvRequestShutdown events, the reception of which -makes the keyboard terminate only if all lock keys are in the off state. We -would then modify the Active state as follows:

      +inner orthogonal regions are in particular states. For this purpose, the +state_cast<>() function introduced under +Getting state +information out of the machine is also available within states.

      +

      As a somewhat far-fetched example, let's assume that our +keyboard also accepts EvRequestShutdown +events, the reception of which makes the keyboard terminate only if all lock +keys are in the off state. We would then modify the Keyboard state machine as +follows:

      struct EvRequestShutdown : fsm::event< EvRequestShutdown > {};
       
       struct NumLockOff;
      @@ -938,16 +974,107 @@ struct Active: fsm::simple_state<
           }
         }
       };
      -

      Just like dynamic_cast, passing a pointer type instead of -reference type results in 0 pointers being returned when the cast fails. Note -also the use of state_downcast instead of state_cast. -Similar to the differences between boost::polymorphic_downcast -and dynamic_cast, state_downcast is a much faster -variant of state_cast and can only be used when the passed type -is a most-derived type. state_cast should only be used if you -want to query an additional base, as under -Getting state -information out of the machine.

      +

      Passing a pointer type instead of reference type results in 0 pointers +being returned instead of std::bad_cast being thrown when the +cast fails. Note also the use of state_downcast<>() instead of +state_cast<>(). Similar to the differences between +boost::polymorphic_downcast<>() and dynamic_cast, +state_downcast<>() is a much faster variant of state_cast<>() +and can only be used when the passed type is a most-derived type. +state_cast<>() should only be used if you want to query an additional +base.

      +

      Custom state queries

      +

      It is often desirable to find out exactly which state(s) a machine +currently resides in. To some extent this is already possible with +state_cast<>() and state_downcast<>() but their utility is +rather limited because both only return a yes/no answer to the question "Are +you in state X?". It is possible to ask more sophisticated questions when you +pass an additional base class rather than a state class to state_cast<>() +but this involves more work (all states need to derive from and implement the +additional base), is slow (under the hood state_cast<>() uses +dynamic_cast), forces projects to compile with C++ RTTI turned on +and has a negative impact on state entry/exit speed.

      +

      Especially for debugging it would be so much more useful being able to ask +"In which state(s) are you?". For this purpose it is possible to iterate over +all active innermost states with state_machine::state_begin() +and state_machine::state_end(). Dereferencing the returned +iterator returns a reference to const state_machine::state_base_type, +the common base of all states. We can thus print the currently active state +configuration as follows (see the Keyboard example for the complete code):

      +
      void DisplayStateConfiguration( const Keyboard & kbd )
      +{
      +  char region = 'a';
      +
      +  for (
      +    Keyboard::state_iterator pLeafState = kbd.state_begin();
      +    pLeafState != kbd.state_end(); ++pLeafState )
      +  {
      +    std::cout << "Orthogonal region " << region << ": ";
      +    std::cout << typeid( *pLeafState ).name() << "\n";
      +    ++region;
      +  }
      +}
      +

      If necessary, the outer states can be accessed with +state_machine::state_base_type::outer_state_ptr(), which returns a +pointer to const state_machine::state_base_type. When called on +an outermost state this function simply returns 0.

      +

      State type information

      +

      To cut down on executable size some applications must be compiled with C++ +RTTI turned off. This would render the ability to iterate over all active +states pretty much useless if it weren't for the following two functions:

      +
        +
      • static unspecified_type simple_state::static_type()
      • +
      • unspecified_type
        +
          state_machine::state_base_type::dynamic_type() const
      • +
      +

      Both return a value that is comparable via operator==() and +std::less. This alone would be enough to implement the +DisplayStateConfiguration() function above without the help of +typeid but it is still somewhat cumbersome as a map must be used to +associate the type information values with the state names.

      +

      Custom state type information

      +

      That's why the following functions are also provided (only available when + +BOOST_FSM_USE_NATIVE_RTTI is not defined):

      +
        +
      • template< class T >
        + static void simple_state::custom_static_type_ptr( const T * );
      • +
      • template< class T >
        + static const T * simple_state::custom_static_type_ptr();
      • +
      • template< class T >
        + const T * state_machine::
        +  state_base_type::custom_dynamic_type_ptr() const;
      • +
      +

      These allow us to directly associate arbitrary state type information with +each state ...

      +
      // ...
      +
      +int main()
      +{
      +  NumLockOn::custom_static_type_ptr( "NumLockOn" );
      +  NumLockOff::custom_static_type_ptr( "NumLockOff" );
      +  CapsLockOn::custom_static_type_ptr( "CapsLockOn" );
      +  CapsLockOff::custom_static_type_ptr( "CapsLockOff" );
      +  ScrollLockOn::custom_static_type_ptr( "ScrollLockOn" );
      +  ScrollLockOff::custom_static_type_ptr( "ScrollLockOff" );
      +
      +  // ...
      +}
      +

      ... and rewrite the display function as follows:

      +
      void DisplayStateConfiguration( const Keyboard & kbd )
      +{
      +  char region = 'a';
      +
      +  for (
      +    Keyboard::state_iterator pLeafState = kbd.state_begin();
      +    pLeafState != kbd.state_end(); ++pLeafState )
      +  {
      +    std::cout << "Orthogonal region " << region << ": ";
      +    std::cout <<
      +      pLeafState->custom_dynamic_type_ptr< char >() << "\n";
      +    ++region;
      +  }
      +}

      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 @@ -1115,12 +1242,12 @@ struct Off : fsm::simple_state<

      Asynchronous state machines

      Why asynchronous state machines are necessary

      As the name suggests, a synchronous state machine processes each event -synchronously. This behavior is implemented by the state_machine<> +synchronously. This behavior is implemented by the state_machine class template, whose process_event() only returns after having executed all reactions (including the ones provoked by internal events that actions might have posted). Moreover, this function is also strictly -non-reentrant (just like all other member functions, so state_machine<> -is not thread-safe). This makes it difficult for two state_machine<> +non-reentrant (just like all other member functions, so state_machine +is not thread-safe). This makes it difficult for two state_machine subclasses to communicate via events in a bi-directional fashion correctly, even in a single-threaded program. For example, state machine A is in the middle of processing an external event. Inside an action, it decides @@ -1134,20 +1261,20 @@ external event and as soon as B answers via the call-back, A::process_event is unavoidably reentered. This all really happens in a single thread, that's why "wait" is in quotes.

      How it works

      -

      In contrast to state_machine<>, -asynchronous_state_machine<> does not have a member function -process_event(). Instead, there is only queue_event(), -which returns immediately after pushing the event into a queue. A worker -thread will later pop the event out of the queue to have it processed. For -applications using the boost::thread library, the necessary locking, unlocking -and waiting logic is readily available in class worker<>.

      -

      Applications will usually first create a worker<> object and -then create one or more asynchronous_state_machine<> subclass +

      In contrast to state_machine, asynchronous_state_machine +does not have a member function process_event(). Instead, there +is only queue_event(), which returns immediately after pushing +the event into a queue. A worker thread will later pop the event out of the +queue to have it processed. For applications using the boost::thread library, +the necessary locking, unlocking and waiting logic is readily available in +class worker.

      +

      Applications will usually first create a worker object and +then create one or more asynchronous_state_machine subclass objects, passing the worker object to the constructor(s). Finally, worker<>::operator()() is either called directly to let the machine(s) run in the current thread, or, a boost::function object referencing operator() is passed to a new boost::thread. -I the following code, we are running one state machine in a new boost::thread +In the following code, we are running one state machine in a new boost::thread and the other in the main thread (see the PingPong example for the full source code):

      // ...
      @@ -1231,9 +1358,9 @@ exceptions that machines fail to handle. In this case all machines are
       terminated before the exception is propagated.

      Caution:

        -
      • asynchronous_state_machine<> subclass objects must not - be destructed before worker::operator()() returns. Moreover, - the worker<> object may be destructed only after all of the +
      • asynchronous_state_machine subclass objects must not be + destructed before worker::operator()() returns. Moreover, the + worker object may be destructed only after all of the registered state machines have been destructed. Violations of these rules will result in failing runtime asserts.
      • The interface of asynchronous_state_machine consists of @@ -1241,13 +1368,13 @@ terminated before the exception is propagated.

        other functions like initiate(), process_event(), etc. are nevertheless also publicly available, but it is not safe to call these functions from any other thread than the worker (over which most users - have no control). asynchronous_state_machine<>::queue_event() - is the only function than can safely be called simultaneously from multiple - threads.
      • + have no control). + asynchronous_state_machine<>::queue_event() is the only function than + can safely be called simultaneously from multiple threads.

      Revised -12 October, 2003

      +12 December, 2003

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

      diff --git a/doc/tutorial.pdf b/doc/tutorial.pdf index 373f1e6..e3cc896 100644 Binary files a/doc/tutorial.pdf and b/doc/tutorial.pdf differ diff --git a/example/BitMachine/BitMachine.cpp b/example/BitMachine/BitMachine.cpp index da70a69..ea1565e 100644 --- a/example/BitMachine/BitMachine.cpp +++ b/example/BitMachine/BitMachine.cpp @@ -10,7 +10,7 @@ ////////////////////////////////////////////////////////////////////////////// const unsigned int noOfBits = 6; -// #define CUSTOMIZE_MEMORY_MANAGEMENT +#define CUSTOMIZE_MEMORY_MANAGEMENT // #define BOOST_FSM_USE_NATIVE_RTTI ////////////////////////////////////////////////////////////////////////////// // This program demonstrates the fact that measures must be taken to hide some diff --git a/example/BitMachine/BitMachine.vcproj b/example/BitMachine/BitMachine.vcproj index b0f4c03..72b85c7 100644 --- a/example/BitMachine/BitMachine.vcproj +++ b/example/BitMachine/BitMachine.vcproj @@ -81,6 +81,7 @@ AdditionalIncludeDirectories=""..\..\..\..\..\boost-sandbox";"..\..\..\..\..\boost-1.30.2"" PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" StringPooling="TRUE" + ExceptionHandling="TRUE" RuntimeLibrary="4" BufferSecurityCheck="FALSE" EnableFunctionLevelLinking="TRUE" diff --git a/example/Keyboard/Keyboard.cpp b/example/Keyboard/Keyboard.cpp index c6072bb..87349ab 100644 --- a/example/Keyboard/Keyboard.cpp +++ b/example/Keyboard/Keyboard.cpp @@ -11,8 +11,9 @@ ////////////////////////////////////////////////////////////////////////////// // The following example program demonstrates the use of orthogonal states and // state_downcast to query the state of orthogonal regions. +// Moreover, the use of the state type information interface is also shown. ////////////////////////////////////////////////////////////////////////////// - +// #define BOOST_FSM_USE_NATIVE_RTTI #include @@ -24,6 +25,7 @@ #include #include +#include namespace fsm = boost::fsm; namespace mpl = boost::mpl; @@ -45,16 +47,6 @@ struct Active: fsm::simple_state< Active, Keyboard, fsm::custom_reaction< EvRequestShutdown >, mpl::list< NumLockOff, CapsLockOff, ScrollLockOff > > { - Active() - { - std::cout << "Entering Active\n"; - } - - ~Active() - { - std::cout << "Exiting Active\n"; - } - fsm::result react( const EvRequestShutdown & ) { if ( ( state_downcast< const NumLockOff * >() != 0 ) && @@ -66,7 +58,7 @@ struct Active: fsm::simple_state< } else { - std::cout << "Ignoring shutdown request\n"; + std::cout << "Ignoring shutdown request\n\n"; return discard_event(); } } @@ -74,82 +66,99 @@ struct Active: fsm::simple_state< struct NumLockOn : fsm::simple_state< NumLockOn, Active::orthogonal< 0 >, - fsm::transition< EvNumLockPressed, NumLockOff > > -{ - NumLockOn() - { - std::cout << "NumLockOn\n"; - } -}; + fsm::transition< EvNumLockPressed, NumLockOff > > {}; struct NumLockOff : fsm::simple_state< NumLockOff, Active::orthogonal< 0 >, - fsm::transition< EvNumLockPressed, NumLockOn > > -{ - NumLockOff() - { - std::cout << "NumLockOff\n"; - } -}; + fsm::transition< EvNumLockPressed, NumLockOn > > {}; struct CapsLockOn : fsm::simple_state< CapsLockOn, Active::orthogonal< 1 >, - fsm::transition< EvCapsLockPressed, CapsLockOff > > -{ - CapsLockOn() - { - std::cout << "CapsLockOn\n"; - } -}; + fsm::transition< EvCapsLockPressed, CapsLockOff > > {}; struct CapsLockOff : fsm::simple_state< CapsLockOff, Active::orthogonal< 1 >, - fsm::transition< EvCapsLockPressed, CapsLockOn > > -{ - CapsLockOff() - { - std::cout << "CapsLockOff\n"; - } -}; + fsm::transition< EvCapsLockPressed, CapsLockOn > > {}; struct ScrollLockOn : fsm::simple_state< ScrollLockOn, Active::orthogonal< 2 >, - fsm::transition< EvScrollLockPressed, ScrollLockOff > > -{ - ScrollLockOn() - { - std::cout << "ScrollLockOn\n"; - } -}; + fsm::transition< EvScrollLockPressed, ScrollLockOff > > {}; struct ScrollLockOff : fsm::simple_state< ScrollLockOff, Active::orthogonal< 2 >, - fsm::transition< EvScrollLockPressed, ScrollLockOn > > + fsm::transition< EvScrollLockPressed, ScrollLockOn > > {}; + + +void DisplayStateConfiguration( const Keyboard & keyboard ) { - ScrollLockOff() + char orthogonalRegion = 'a'; + + for ( Keyboard::state_iterator pLeafState = keyboard.state_begin(); + pLeafState != keyboard.state_end(); ++pLeafState ) { - std::cout << "ScrollLockOff\n"; + std::cout << "Orthogonal region " << orthogonalRegion << ": "; + + const Keyboard::state_base_type * pState = &*pLeafState; + + while ( pState != 0 ) + { + if ( pState != &*pLeafState ) + { + std::cout << " -> "; + } + + #ifdef BOOST_FSM_USE_NATIVE_RTTI + std::cout << std::setw( 15 ) << typeid( *pState ).name(); + #else + std::cout << std::setw( 15 ) << + pState->custom_dynamic_type_ptr< char >(); + #endif + pState = pState->outer_state_ptr(); + } + + std::cout << "\n"; + ++orthogonalRegion; } -}; + + std::cout << "\n"; +} + int main() { + #ifndef BOOST_FSM_USE_NATIVE_RTTI + Active::custom_static_type_ptr( "Active" ); + NumLockOn::custom_static_type_ptr( "NumLockOn" ); + NumLockOff::custom_static_type_ptr( "NumLockOff" ); + CapsLockOn::custom_static_type_ptr( "CapsLockOn" ); + CapsLockOff::custom_static_type_ptr( "CapsLockOff" ); + ScrollLockOn::custom_static_type_ptr( "ScrollLockOn" ); + ScrollLockOff::custom_static_type_ptr( "ScrollLockOff" ); + #endif + std::cout << "boost::fsm Keyboard example\n\n"; Keyboard keyboard; keyboard.initiate(); + DisplayStateConfiguration( keyboard ); keyboard.process_event( EvNumLockPressed() ); + DisplayStateConfiguration( keyboard ); keyboard.process_event( EvRequestShutdown() ); keyboard.process_event( EvCapsLockPressed() ); + DisplayStateConfiguration( keyboard ); keyboard.process_event( EvRequestShutdown() ); keyboard.process_event( EvScrollLockPressed() ); + DisplayStateConfiguration( keyboard ); keyboard.process_event( EvRequestShutdown() ); keyboard.process_event( EvNumLockPressed() ); + DisplayStateConfiguration( keyboard ); keyboard.process_event( EvRequestShutdown() ); keyboard.process_event( EvCapsLockPressed() ); + DisplayStateConfiguration( keyboard ); keyboard.process_event( EvRequestShutdown() ); keyboard.process_event( EvScrollLockPressed() ); + DisplayStateConfiguration( keyboard ); keyboard.process_event( EvRequestShutdown() ); return 0; diff --git a/example/Keyboard/Keyboard.vcproj b/example/Keyboard/Keyboard.vcproj index 0477573..5cc7efb 100644 --- a/example/Keyboard/Keyboard.vcproj +++ b/example/Keyboard/Keyboard.vcproj @@ -87,6 +87,7 @@ DisableLanguageExtensions="TRUE" TreatWChar_tAsBuiltInType="TRUE" ForceConformanceInForLoopScope="TRUE" + RuntimeTypeInfo="TRUE" UsePrecompiledHeader="0" WarningLevel="4" WarnAsError="TRUE" @@ -151,6 +152,9 @@ + + diff --git a/example/PingPong/PingPong.vcproj b/example/PingPong/PingPong.vcproj index 7e9e52c..4844aef 100644 --- a/example/PingPong/PingPong.vcproj +++ b/example/PingPong/PingPong.vcproj @@ -160,9 +160,6 @@ - - diff --git a/example/StopWatch/StopWatch.vcproj b/example/StopWatch/StopWatch.vcproj index fda40c8..7eb8a05 100644 --- a/example/StopWatch/StopWatch.vcproj +++ b/example/StopWatch/StopWatch.vcproj @@ -158,9 +158,6 @@ - - diff --git a/include/boost/statechart/asynchronous_state_machine.hpp b/include/boost/statechart/asynchronous_state_machine.hpp index fc84011..3188e04 100644 --- a/include/boost/statechart/asynchronous_state_machine.hpp +++ b/include/boost/statechart/asynchronous_state_machine.hpp @@ -38,13 +38,15 @@ class asynchronous_state_machine : public state_machine< typedef state_machine< MostDerived, InitialState, Allocator, ExceptionTranslator > machine_base; typedef detail::event_processor< Worker > processor_base; - public: + protected: ////////////////////////////////////////////////////////////////////////// asynchronous_state_machine( Worker & myWorker ) : processor_base( myWorker ) { } + virtual ~asynchronous_state_machine() {} + public: ////////////////////////////////////////////////////////////////////////// // The following declarations should be private. diff --git a/include/boost/statechart/detail/counted_base.hpp b/include/boost/statechart/detail/counted_base.hpp index 5209434..89f6d1b 100644 --- a/include/boost/statechart/detail/counted_base.hpp +++ b/include/boost/statechart/detail/counted_base.hpp @@ -48,8 +48,6 @@ class counted_base : private locked_base< NeedsLocking > typedef locked_base< NeedsLocking > base_type; public: ////////////////////////////////////////////////////////////////////////// - virtual ~counted_base() {} - bool ref_counted() const { return count_ != 0; @@ -58,6 +56,7 @@ class counted_base : private locked_base< NeedsLocking > protected: ////////////////////////////////////////////////////////////////////////// counted_base() : count_( 0 ) {} + virtual ~counted_base() {} // do nothing copy implementation is intentional (the number of // referencing pointers of the source and the destination is not changed diff --git a/include/boost/statechart/detail/leaf_state.hpp b/include/boost/statechart/detail/leaf_state.hpp index 97ef4fd..da8f83d 100644 --- a/include/boost/statechart/detail/leaf_state.hpp +++ b/include/boost/statechart/detail/leaf_state.hpp @@ -30,7 +30,14 @@ class leaf_state : public state_base< Allocator, RttiPolicy > typedef state_base< Allocator, RttiPolicy > base_type; protected: ////////////////////////////////////////////////////////////////////////// - leaf_state( typename RttiPolicy::id_type id ) : base_type( id ) {} + leaf_state( + typename RttiPolicy::id_provider_type idProvider + ) : + base_type( idProvider ) + { + } + + virtual ~leaf_state() {} public: ////////////////////////////////////////////////////////////////////////// diff --git a/include/boost/statechart/detail/node_state.hpp b/include/boost/statechart/detail/node_state.hpp index f477f51..c4a1b9b 100644 --- a/include/boost/statechart/detail/node_state.hpp +++ b/include/boost/statechart/detail/node_state.hpp @@ -33,7 +33,10 @@ class node_state : public state_base< Allocator, RttiPolicy > typedef state_base< Allocator, RttiPolicy > base_type; protected: ////////////////////////////////////////////////////////////////////////// - node_state( typename RttiPolicy::id_type id ) : base_type( id ) + node_state( + typename RttiPolicy::id_provider_type idProvider + ) : + base_type( idProvider ) { for ( orthogonal_position_type pos = 0; pos < noOfOrthogonalRegions; ++pos ) @@ -42,6 +45,8 @@ class node_state : public state_base< Allocator, RttiPolicy > } } + virtual ~node_state() {} + public: ////////////////////////////////////////////////////////////////////////// // The following declarations should be private. diff --git a/include/boost/statechart/detail/rtti_policy.hpp b/include/boost/statechart/detail/rtti_policy.hpp index 9ae1b81..915ca9d 100644 --- a/include/boost/statechart/detail/rtti_policy.hpp +++ b/include/boost/statechart/detail/rtti_policy.hpp @@ -11,6 +11,7 @@ #include +#include #include // std::type_info @@ -34,28 +35,24 @@ namespace detail -// #define BOOST_FSM_USE_NATIVE_RTTI - -#ifndef BOOST_FSM_USE_NATIVE_RTTI ////////////////////////////////////////////////////////////////////////////// -template< class MostDerived > struct id_provider { - static bool dummy_; + const void * pCustomId_; + #if defined( BOOST_ENABLE_ASSERT_HANDLER ) || defined( _DEBUG ) + const std::type_info * pCustomIdType_; + #endif }; -template< class MostDerived > -bool id_provider< MostDerived >::dummy_; - template< class MostDerived > struct id_holder { - static void * pId_; + static id_provider idProvider_; }; template< class MostDerived > -void * id_holder< MostDerived >::pId_ = &id_provider< MostDerived >::dummy_; -#endif +id_provider id_holder< MostDerived >::idProvider_; + ////////////////////////////////////////////////////////////////////////////// @@ -66,40 +63,31 @@ struct rtti_policy { public: //////////////////////////////////////////////////////////////////////// - id_type( const std::type_info & id ) : id_( id ) {} + explicit id_type( const std::type_info & id ) : id_( id ) {} - friend bool operator==( id_type left, id_type right ) + bool operator==( id_type right ) const { - return left.id_ == right.id_ != 0; - } - friend bool operator!=( id_type left, id_type right ) - { - return !( left == right ); + return id_ == right.id_ != 0; } + bool operator!=( id_type right ) const { return !( *this == right ); } - friend bool operator<( id_type left, id_type right ) + bool operator<( id_type right ) const { - return left.id_.before( right.id_ ) != 0; - } - friend bool operator>( id_type left, id_type right ) - { - return right < left; - } - friend bool operator>=( id_type left, id_type right ) - { - return !( left < right ); - } - friend bool operator<=( id_type left, id_type right ) - { - return !( right < left ); + return id_.before( right.id_ ) != 0; } + bool operator>( id_type right ) const { return right < *this; } + bool operator>=( id_type right ) const { return !( *this < right ); } + bool operator<=( id_type right ) const { return !( right < *this ); } private: //////////////////////////////////////////////////////////////////////// const std::type_info & id_; }; + + typedef bool id_provider_type; // dummy #else - typedef void * id_type; + typedef const void * id_type; + typedef const id_provider & id_provider_type; #endif //////////////////////////////////////////////////////////////////////////// @@ -111,22 +99,40 @@ struct rtti_policy id_type dynamic_type() const { #ifdef BOOST_FSM_USE_NATIVE_RTTI - return typeid( *this ); + return id_type( typeid( *this ) ); #else - return id_; + return &idProvider_; #endif } + #ifndef BOOST_FSM_USE_NATIVE_RTTI + template< typename CustomId > + const CustomId * custom_dynamic_type_ptr() const + { + BOOST_ASSERT( + ( idProvider_.pCustomIdType_ == 0 ) || + ( *idProvider_.pCustomIdType_ == typeid( CustomId ) ) ); + return static_cast< const CustomId * >( idProvider_.pCustomId_ ); + } + #endif + protected: //////////////////////////////////////////////////////////////////////// + virtual ~base_type() {} + #ifdef BOOST_FSM_USE_NATIVE_RTTI - base_type( id_type ) {} + base_type( id_provider_type ) {} #else - base_type( id_type id ) : id_( id ) {} + base_type( + id_provider_type idProvider + ) : + idProvider_( idProvider ) + { + } private: //////////////////////////////////////////////////////////////////////// - const id_type id_; + id_provider_type idProvider_; #endif }; @@ -139,15 +145,44 @@ struct rtti_policy static id_type static_type() { #ifdef BOOST_FSM_USE_NATIVE_RTTI - return typeid( const MostDerived ); + return id_type( typeid( const MostDerived ) ); #else - return id_holder< MostDerived >::pId_; + return &id_holder< MostDerived >::idProvider_; #endif } + #ifndef BOOST_FSM_USE_NATIVE_RTTI + template< class CustomId > + static const CustomId * custom_static_type_ptr() + { + BOOST_ASSERT( + ( id_holder< MostDerived >::idProvider_.pCustomIdType_ == 0 ) || + ( *id_holder< MostDerived >::idProvider_.pCustomIdType_ == + typeid( CustomId ) ) ); + return static_cast< const CustomId * >( + id_holder< MostDerived >::idProvider_.pCustomId_ ); + } + + template< class CustomId > + static void custom_static_type_ptr( const CustomId * pCustomId ) + { + #if defined( BOOST_ENABLE_ASSERT_HANDLER ) || defined( _DEBUG ) + id_holder< MostDerived >::idProvider_.pCustomIdType_ = + &typeid( CustomId ); + #endif + id_holder< MostDerived >::idProvider_.pCustomId_ = pCustomId; + } + #endif + protected: //////////////////////////////////////////////////////////////////////// - derived_type() : Base( static_type() ) {} + virtual ~derived_type() {} + + #ifdef BOOST_FSM_USE_NATIVE_RTTI + derived_type() : Base( false ) {} + #else + derived_type() : Base( id_holder< MostDerived >::idProvider_ ) {} + #endif }; }; diff --git a/include/boost/statechart/detail/state_base.hpp b/include/boost/statechart/detail/state_base.hpp index a091cc9..ba9674f 100644 --- a/include/boost/statechart/detail/state_base.hpp +++ b/include/boost/statechart/detail/state_base.hpp @@ -64,19 +64,30 @@ class state_base : private noncopyable, public RttiPolicy::base_type< { typedef typename RttiPolicy::base_type< counted_base< orthogonal_position_type, false > > base_type; + + public: + ////////////////////////////////////////////////////////////////////////// + // Returns a pointer to the immediate outer state _if_ there is one, + // returns 0 otherwise (this is the outermost state then) + virtual const state_base * outer_state_ptr() const = 0; + protected: ////////////////////////////////////////////////////////////////////////// - // The following declarations should be private. - // They are only protected because many compilers lack template friends. - ////////////////////////////////////////////////////////////////////////// - state_base( typename RttiPolicy::id_type id ) : - base_type( id ), + state_base( typename RttiPolicy::id_provider_type idProvider ) : + base_type( idProvider ), reactionEnabled_( false ), deferredEvents_( false ), terminationState_( false ) { } + virtual ~state_base() {} + + protected: + ////////////////////////////////////////////////////////////////////////// + // The following declarations should be private. + // They are only protected because many compilers lack template friends. + ////////////////////////////////////////////////////////////////////////// void enable_reaction() { reactionEnabled_ = true; @@ -129,10 +140,6 @@ class state_base : private noncopyable, public RttiPolicy::base_type< const event_base & evt, typename RttiPolicy::id_type eventType ) = 0; - // returns a pointer to the immediate outer state _if_ there is one, - // returns 0 otherwise (this is the outermost state then) - virtual state_base * outer_state_ptr() const = 0; - typedef intrusive_ptr< state_base > state_base_ptr_type; typedef std::list< state_base_ptr_type, diff --git a/include/boost/statechart/event.hpp b/include/boost/statechart/event.hpp index a2b0ab1..865a4fd 100644 --- a/include/boost/statechart/event.hpp +++ b/include/boost/statechart/event.hpp @@ -10,12 +10,8 @@ +#include #include -#include - -#include -#include -#include @@ -26,29 +22,6 @@ namespace fsm -////////////////////////////////////////////////////////////////////////////// -class event_base : public detail::rtti_policy::base_type< - detail::counted_base< unsigned int > > -{ - typedef detail::rtti_policy::base_type< - detail::counted_base< unsigned int > > base_type; - public: - ////////////////////////////////////////////////////////////////////////// - intrusive_ptr< const event_base > clone() const - { - BOOST_ASSERT( base_type::ref_counted() ); - return intrusive_ptr< const event_base >( this ); - } - - protected: - ////////////////////////////////////////////////////////////////////////// - event_base( detail::rtti_policy::id_type id ) : - base_type( id ) - { - } -}; - - ////////////////////////////////////////////////////////////////////////////// template< class MostDerived > class event : public detail::rtti_policy::derived_type< diff --git a/include/boost/statechart/event_base.hpp b/include/boost/statechart/event_base.hpp new file mode 100644 index 0000000..1391a36 --- /dev/null +++ b/include/boost/statechart/event_base.hpp @@ -0,0 +1,57 @@ +#ifndef BOOST_FSM_EVENT_BASE_HPP_INCLUDED +#define BOOST_FSM_EVENT_BASE_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 +#include + +#include +#include + + + +namespace boost +{ +namespace fsm +{ + + + +////////////////////////////////////////////////////////////////////////////// +class event_base : public detail::rtti_policy::base_type< + detail::counted_base< unsigned int > > +{ + typedef detail::rtti_policy::base_type< + detail::counted_base< unsigned int > > base_type; + public: + ////////////////////////////////////////////////////////////////////////// + intrusive_ptr< const event_base > clone() const + { + BOOST_ASSERT( base_type::ref_counted() ); + return intrusive_ptr< const event_base >( this ); + } + + protected: + ////////////////////////////////////////////////////////////////////////// + event_base( detail::rtti_policy::id_provider_type idProvider ) : + base_type( idProvider ) + { + } +}; + + + +} // namespace fsm +} // namespace boost + + + +#endif diff --git a/include/boost/statechart/simple_state.hpp b/include/boost/statechart/simple_state.hpp index 076c599..1b104c3 100644 --- a/include/boost/statechart/simple_state.hpp +++ b/include/boost/statechart/simple_state.hpp @@ -174,9 +174,9 @@ enum history_mode ////////////////////////////////////////////////////////////////////////////// template< class MostDerived, - class Context, // either an outer state or a state_machine + class Context, class Reactions = no_reactions, - class InnerInitial = detail::empty_list, // initial inner state + class InnerInitial = detail::empty_list, history_mode historyMode = has_no_history > class simple_state : public detail::simple_state_base_type< MostDerived, typename Context::inner_context_type, InnerInitial >::type @@ -188,19 +188,6 @@ class simple_state : public detail::simple_state_base_type< MostDerived, public: ////////////////////////////////////////////////////////////////////////// typedef typename Context::inner_context_type context_type; - BOOST_STATIC_CONSTANT( - detail::orthogonal_position_type, - orthogonal_position = Context::inner_orthogonal_position ); - typedef typename context_type::event_base_ptr_type event_base_ptr_type; - - // If you receive a - // "use of undefined type 'boost::STATIC_ASSERTION_FAILURE'" or similar - // compiler error here then this state resides in a non-existent - // orthogonal region of the outer state. - BOOST_STATIC_ASSERT( - orthogonal_position < context_type::no_of_orthogonal_regions ); - - typedef simple_state my_base; template< detail::orthogonal_position_type innerOrthogonalPosition > struct orthogonal @@ -211,10 +198,21 @@ class simple_state : public detail::simple_state_base_type< MostDerived, typedef MostDerived inner_context_type; }; + typedef typename context_type::outermost_context_type + outermost_context_type; + + outermost_context_type & outermost_context() + { + BOOST_ASSERT( get_pointer( pContext_ ) != 0 ); + return pContext_->outermost_context(); + } + + const outermost_context_type & outermost_context() const + { + BOOST_ASSERT( get_pointer( pContext_ ) != 0 ); + return pContext_->outermost_context(); + } - // Returns a reference to the context identified by the template - // parameter. This can either be _this_ object or one of its - // direct or indirect contexts template< class OtherContext > OtherContext & context() { @@ -227,19 +225,12 @@ class simple_state : public detail::simple_state_base_type< MostDerived, return context_impl( static_cast< OtherContext * >( 0 ) ); } - void post_event( const event_base_ptr_type & pEvent ) - { - outermost_context().post_event( pEvent ); - } - - // see state_machine class for documentation template< class Target > Target state_cast() const { return outermost_context().template state_cast< Target >(); } - // see state_machine class for documentation template< class Target > Target state_downcast() const { @@ -247,6 +238,13 @@ class simple_state : public detail::simple_state_base_type< MostDerived, } + typedef typename context_type::event_base_ptr_type event_base_ptr_type; + + void post_event( const event_base_ptr_type & pEvent ) + { + outermost_context().post_event( pEvent ); + } + result discard_event() { state_base_type::reaction_initiated(); @@ -266,7 +264,6 @@ class simple_state : public detail::simple_state_base_type< MostDerived, return do_defer_event; } - // Initiates a transition to DestinationState (without transition action). template< class DestinationState > result transit() { @@ -274,9 +271,6 @@ class simple_state : public detail::simple_state_base_type< MostDerived, detail::no_transition_function() ); } - // 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 DestinationState, class TransitionContext, class Event > result transit( void ( TransitionContext::*pTransitionAction )( const Event & ), @@ -287,9 +281,6 @@ class simple_state : public detail::simple_state_base_type< MostDerived, pTransitionAction, evt ) ); } - // Terminates this state. Depending on whether there are other orthogonal - // states present this may or may not lead to the whole statemachine being - // terminated. result terminate() { state_base_type::reaction_initiated(); @@ -324,6 +315,17 @@ class simple_state : public detail::simple_state_base_type< MostDerived, // The following declarations should be private. // They are only public because many compilers lack template friends. ////////////////////////////////////////////////////////////////////////// + BOOST_STATIC_CONSTANT( + detail::orthogonal_position_type, + orthogonal_position = Context::inner_orthogonal_position ); + + // If you receive a + // "use of undefined type 'boost::STATIC_ASSERTION_FAILURE'" or similar + // compiler error here then this state resides in a non-existent + // orthogonal region of the outer state. + BOOST_STATIC_ASSERT( + orthogonal_position < context_type::no_of_orthogonal_regions ); + typedef MostDerived inner_context_type; BOOST_STATIC_CONSTANT( detail::orthogonal_position_type, @@ -393,29 +395,13 @@ class simple_state : public detail::simple_state_base_type< MostDerived, return reactionResult; } - virtual state_base_type * outer_state_ptr() const + virtual const state_base_type * outer_state_ptr() const { return outer_state_ptr_impl< is_same< outermost_context_type, context_type >::value >(); } - outermost_context_type & outermost_context() - { - BOOST_ASSERT( get_pointer( pContext_ ) != 0 ); - return pContext_->outermost_context(); - } - - const outermost_context_type & outermost_context() const - { - BOOST_ASSERT( get_pointer( pContext_ ) != 0 ); - return pContext_->outermost_context(); - } - - - // Returns a pointer to the direct or indirect context identified by the - // template parameter. In contrast to context(), this function cannot - // return a pointer to this object. template< class OtherContext > const typename OtherContext::inner_context_ptr_type & context_ptr() const { @@ -423,20 +409,12 @@ class simple_state : public detail::simple_state_base_type< MostDerived, } - // Given a outermost context (i.e. a state machine), constructs this state - // with all its outer and inner initial states. Used for initial - // construction only. - // After each successful (non-throwing) construction the current state is - // reported back to the state machine. static void initial_deep_construct( outermost_context_type & outermostContext ) { deep_construct( &outermostContext, outermostContext ); } - // Constructs this state with all its inner initial states. - // After each successful (non-throwing) construction the current state is - // reported back to the state machine. static void deep_construct( const context_ptr_type & pContext, outermost_context_type & outermostContext ) @@ -593,9 +571,9 @@ class simple_state : public detail::simple_state_base_type< MostDerived, common_context_type, DestinationState >::type context_list_type; // If you receive a - // "use of undefined type 'boost::STATIC_ASSERTION_FAILURE'" or similar - // compiler error here then you tried to make an invalid transition - // between different orthogonal regions. + // "use of undefined type 'boost::STATIC_ASSERTION_FAILURE'" or + // similar compiler error here then you tried to make an invalid + // transition between different orthogonal regions. BOOST_STATIC_ASSERT( termination_state_type::orthogonal_position == mpl::front< context_list_type >::type::orthogonal_position ); @@ -633,13 +611,13 @@ class simple_state : public detail::simple_state_base_type< MostDerived, } template< bool isOutermost > - state_base_type * outer_state_ptr_impl() const + const state_base_type * outer_state_ptr_impl() const { return get_pointer( pContext_ ); } template<> - state_base_type * outer_state_ptr_impl< true >() const + const state_base_type * outer_state_ptr_impl< true >() const { return 0; } diff --git a/include/boost/statechart/state.hpp b/include/boost/statechart/state.hpp index 7e1fbb3..1dd953a 100644 --- a/include/boost/statechart/state.hpp +++ b/include/boost/statechart/state.hpp @@ -22,9 +22,9 @@ namespace fsm template< class MostDerived, - class Context, // either an outer state or a state_machine + class Context, class Reactions = no_reactions, - class InnerInitial = detail::empty_list, // initial inner state + class InnerInitial = detail::empty_list, history_mode historyMode = has_no_history > class state : public simple_state< MostDerived, Context, Reactions, InnerInitial, historyMode > @@ -50,6 +50,8 @@ class state : public simple_state< base_type::set_context( ctx.pContext_ ); } + virtual ~state() {} + public: ////////////////////////////////////////////////////////////////////////// // The following declarations should be private. diff --git a/include/boost/statechart/state_machine.hpp b/include/boost/statechart/state_machine.hpp index b334358..e2809bd 100644 --- a/include/boost/statechart/state_machine.hpp +++ b/include/boost/statechart/state_machine.hpp @@ -42,6 +42,7 @@ #include // std::allocator #include // std::bad_cast #include // std::less +#include @@ -223,8 +224,6 @@ class history_key ////////////////////////////////////////////////////////////////////////////// -// Base class for all state machines -// Some function names were derived from a state machine by Aleksey Gurtovoy. template< class MostDerived, class InitialState, class Allocator = std::allocator< void >, @@ -238,9 +237,6 @@ class state_machine : noncopyable typedef event_base event_base_type; typedef intrusive_ptr< const event_base_type > event_base_ptr_type; - // Initiates the state machine. - // Returns true if the state machine terminated as a direct or indirect - // result of entering the initial state. bool initiate() { terminate(); @@ -263,9 +259,6 @@ class state_machine : noncopyable return currentStates_.size() == 0; } - // Processes the passed event. - // Returns true if the state machine was running before and terminated - // after processing the event. bool process_event( const event_base_type & evt ) { const bool running = !terminated(); @@ -274,16 +267,6 @@ class state_machine : noncopyable return terminated() && running; } - // Has semantics similar to dynamic_cast. Returns a pointer or a reference - // to a state with the specified type _if_ the machine is currently in - // such a state. Can _cross_ inheritance trees, e.g. if the machine is in - // state A, which derives from simple_state _and_ B then - // state_cast< const B & >() will return a reference to B and - // state_cast< const A & >() will return a reference to A. - // Target can take either of the following forms: - // - const X &: throws std::bad_cast if the machine is not currently in - // state X - // - const X *: returns 0 if the machine is not currently in state X template< class Target > Target state_cast() const { @@ -324,18 +307,6 @@ class state_machine : noncopyable return impl::not_found< Target >(); } - // Typically much faster variant of state_cast. Returns a pointer or a - // reference to a state with the specified type _if_ the machine is - // currently in such a state. - // Does _not_ cross inheritance trees, i.e. Target must be a most-derived - // type, e.g. if the machine is in state A, which derives from - // simple_state _and_ B then state_downcast< const B & >() will not - // compile while state_downcast< const A & >() will return a reference to - // A. - // Target can take either of the following forms: - // - const X &: throws std::bad_cast if the machine is not currently in - // state X - // - const X *: returns 0 if the machine is not currently in state X template< class Target > Target state_downcast() const { @@ -368,6 +339,55 @@ class state_machine : noncopyable return impl::not_found< Target >(); } + typedef detail::state_base< allocator_type, rtti_policy_type > + state_base_type; + + class state_iterator : public std::iterator< + std::forward_iterator_tag, + state_base_type, std::ptrdiff_t, + const state_base_type *, const state_base_type & > + { + public: + ////////////////////////////////////////////////////////////////////// + explicit state_iterator( + state_base_type::state_list_type::const_iterator baseIterator + ) : baseIterator_( baseIterator ) {} + + const state_base_type & operator*() const { return **baseIterator_; } + const state_base_type * operator->() const + { + return &**baseIterator_; + } + + state_iterator & operator++() { ++baseIterator_; return *this; } + state_iterator operator++( int ) + { + return state_iterator( baseIterator_++ ); + } + + bool operator==( const state_iterator & right ) const + { + return baseIterator_ == right.baseIterator_; + } + bool operator!=( const state_iterator & right ) const + { + return !( *this == right ); + } + + private: + state_base_type::state_list_type::const_iterator baseIterator_; + }; + + state_iterator state_begin() const + { + return state_iterator( currentStates_.begin() ); + } + + state_iterator state_end() const + { + return state_iterator( currentStates_.end() ); + } + protected: ////////////////////////////////////////////////////////////////////////// state_machine() : @@ -375,7 +395,7 @@ class state_machine : noncopyable { } - // This destructor was only made virtual, so that that + // This destructor was only made virtual so that that // polymorphic_downcast can be used to cast to MostDerived. virtual ~state_machine() {} @@ -384,9 +404,6 @@ class state_machine : noncopyable // The following declarations should be private. // They are only public because many compilers lack template friends. ////////////////////////////////////////////////////////////////////////// - typedef detail::state_base< allocator_type, rtti_policy_type > - state_base_type; - typedef MostDerived inner_context_type; BOOST_STATIC_CONSTANT( detail::orthogonal_position_type, diff --git a/include/boost/statechart/worker.hpp b/include/boost/statechart/worker.hpp index bfae64e..b9c21f2 100644 --- a/include/boost/statechart/worker.hpp +++ b/include/boost/statechart/worker.hpp @@ -121,8 +121,7 @@ class worker : noncopyable typedef detail::event_processor< worker > processor_type; typedef std::list< processor_type *, Allocator > processor_list_type; - typedef intrusive_ptr< const event_base > - event_ptr_type; + typedef intrusive_ptr< const event_base > event_ptr_type; typedef std::pair< processor_type *, event_ptr_type > queue_element; typedef std::list< queue_element, Allocator > event_queue_type;