From 2944fac99787ad2d37cd3e2c8b166d56735c52f5 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Fri, 30 May 2003 15:12:19 +0000 Subject: [PATCH] Various small changes and bug fixes [SVN r18613] --- doc/DispatchCost.gif | Bin 4702 -> 0 bytes doc/acknowledgments.html | 20 +- doc/index.html | 21 +- doc/rationale.html | 380 ++++++++++-------- doc/tutorial.html | 140 ++++--- example/BitMachine/BitMachine.cpp | 52 ++- example/BitMachine/BitMachine.vcproj | 11 +- example/BitMachine/UniqueObjectAllocator.hpp | 3 +- example/Camera/Camera.hpp | 8 +- example/Camera/Camera.vcproj | 3 + example/Camera/Shooting.hpp | 2 +- example/Handcrafted/Handcrafted.cpp | 3 +- example/Handcrafted/Handcrafted.vcproj | 3 +- example/StateSize/StateSize.cpp | 33 +- example/StateSize/StateSize.vcproj | 3 + example/StopWatch/StopWatch.cpp | 4 +- example/StopWatch/StopWatch.vcproj | 7 +- include/boost/statechart/custom_reaction.hpp | 9 +- include/boost/statechart/deferal.hpp | 9 +- .../boost/statechart/detail/counted_base.hpp | 2 +- .../boost/statechart/detail/leaf_state.hpp | 12 +- .../boost/statechart/detail/node_state.hpp | 33 +- .../boost/statechart/detail/state_base.hpp | 45 ++- .../statechart/detail/universal_state.hpp | 13 +- include/boost/statechart/event.hpp | 34 +- .../boost/statechart/exception_translator.hpp | 5 +- include/boost/statechart/rtti_policy.hpp | 88 ++++ include/boost/statechart/simple_state.hpp | 64 +-- include/boost/statechart/state.hpp | 2 +- include/boost/statechart/state_machine.hpp | 235 ++++++----- include/boost/statechart/termination.hpp | 8 +- include/boost/statechart/transition.hpp | 18 +- 32 files changed, 762 insertions(+), 508 deletions(-) delete mode 100644 doc/DispatchCost.gif create mode 100644 include/boost/statechart/rtti_policy.hpp diff --git a/doc/DispatchCost.gif b/doc/DispatchCost.gif deleted file mode 100644 index 60389d99e89caa7142f977dd721d9c321e470229..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4702 zcmZ?wbh9u|^kFn){LTOZ4Gatn4GaxnssYS8aNxj!I|r7X`rx3zkf6ZOpun&|f#HAx z!vh5d1_uTO2L=ZRh6D$O1_y=(4h#nz7#=tT z0Thr837{BgSOD^L!vlr}289L%g$4(Oh6II%28D(N3JnJo8XkayQUT=fh6Ipv8Ww;o zX?Oq*UxkDQhlGZNgocKMh6M=?2ND_{fCF8j0p#?A1`t@#&~Tuk;Xy+K!-58d1t9+< zENEz0(6C@Z!+`}255Uo*Z~)}VgaaU@3l20KIMDC_9A^p-Kt4}+0MftULBoLu4G%yO zc|ZY_1P&yCBKE)nP;?!50FFfkg#!)>2NDzxG$@~+@*_y3|h3eI%pEZ4A0HW&d#zmuKewRP3i)e)Ps?w;DZ`uh5W!(Fo8+uk%*FG*p#(bTZ=cI)@FJgX1e z*4^FBFPJl{l7Ic(y*Yom$!<0wIC)_4Zs4(MvCH+p2%>+}=xZlj=PSKC8W~I$hAU&Ro{t z^HkyIr_&ij+U^;=%Y3CWS)676;_A;2S5BPd~(zGI;%%u%ppk*heo!jEpd(prV$e}h5l_$XchZe7EmDJ=CZIw zyv*WaG26sNb2|8HZzObRI4!yBxUcm3BS$T*6AyYU+Cq{Wbe_F<>~8(*`s03=ERiL3 z&SEQ{w0J6IUYQ)=rulSANLc1l|HW%Px*}uBUOt_k(5CrpM)b#WH1Q=i#p)pYXKRZ@1lX>zvXf5V(8h z>NZpWVH0UPvog?{RRK!SR(fP{1vK~BzMj_ z?*Oaewfuu!AKho}7tQLDJTmL()VMCL?68#?y7#6WIn4ISy~ycN)tL|WN6+4ncjUNn z{b3&O(j3VpCX0Ul)KblEalh)g?9O>+CwnRmbnzdvIJU;O&T`eXX1S_I zQ@G=HyV)s-#PzKiw){&DAzRMYv`Pr@zJ|kwd=CIx(Hx##;|bFs*EU*qrFHZmHPa zpC8)8eob>td$CgL(-}>MhOUsF<44rmPR1H@o^k20&{F17(PNl#n)CbB8JdqS)L5mQ zIFNH<(_sVN)cBbD?8?s#jD%d0(w78EJr+OK5_6`P<)XyVCoE3;Yt~uC%J4Hym~qtN z(6N-9S&RR^$`Dvk`CQI3VlJ-=@5xznK29vIJ#t!JJjwHg!UNZQncGD&l_v$o3VL}p zZ&h3in=pM%kd>0oQl`L@6IS%HPx-eqO6TOXle=?_6l1lgv>e&yZ8B*>xTwo)1L-Sf z!KaLxx5RYZ$@BDlb?5EOV>U}o)}D|Ib2?$e_hqiU_6_f;HJhw67?&%tEBX0O`aENz zr+3;L!`WH$_$L3jvdpPYb7y()nR(w!)btBw=DXSq~fSFYkl`CW5sIbMIP>6LH9Nm&t*u_@MwO1WZJGNohO1<$QbT2=;do! zeCg8*o$Z;In_rbES#4CV{=gj2=W|8DBKMlV>LZJ`FKdkUF-Cj#MNL?ul)X|gQ_J~_ zk-*utEvuxLuKc}$OK>e0%k*^z&)WWSQkvHAbyGDZzpK|P{B~?z^Hu((Oo!O} zLZ$6eUluUCW?V}P4L|%ZIVoIct(foa%f}X(Z8ge``W^L~|Nq`!Yc?>woSEc4De`z# z|K>|IYB}sen-}FR*|FVMBX9ZXD+kL$w?r`KW(G3deQ_>i>;1c#&e^Zsp4e^JHtX!v zO3jYA$4U8nwQ|;%ANIa`bys-lr@8r#4}LpMRZ`iz<&i$`=hgQoKQrE5@&-T2H|*V1ai6R3;u7_3 z`W07aJd(0c={ajPqnI_ug~@tJuR!x-Iqx;M`~PikD?c(rPPj5bN&Ut#g<9zo$6OUI zC`?NB?K6}Jl?iK~@G0##>j!76l~0wFD<;OYt#&?~tz$@icL+r^?H@jk-@y zQqGtb<|8@(e`UR$Ux)7MukjaN6dfyaGnw^zPeL2Z!yXMP~Pn{lR5Zo@Tuc+x-2igqYcjdATXE`q8%~9&f8y z-kX~e?s4Y=i}{+h=?}~7C63jtGTU-|X7-XzB1e9dW%`82W(Mqzx_k5Ax9X?4FYCGI z++ldHaqw?#Zi8{y!(y|I^DoyHSSL$AV%@)W&63=YkM-uc8SoO|mZ&)6M-_cE!isD^aVE zo*ka4^mUi}-*;;kTg8^|xU<6T)k#^Fxh zwRd&3&okefpZKmRze4R>SI*PB=Nhyp>~5NtvTVQR!_>I^%P(U$aC+KieBKp4K`3SC zfxuH@c_j_ILgziMD6f6g87ugj>HCH=fA_p)|8z58`SG_C@4t9e9QUp3!-c!{Qw&_= z(*+j+{{{>K&bngfc55>=ng?hI&=xc;quedV&MZTbsEPh@Pju4q!w z$rNkKs{3zR8y;RO*yy@hRZ0Ju>rC-1&+p|Nj?$-HqZWOw`2AESGO2D=dmZbFx+P7? z7K@TOFE@O2YkJf!u}KNG7Zi6nH2ZAH?*fW@J>u|-INk>tU39+y!f}M8>IpwH|lMSeQ$*-J#uXJPA>~q z3k*FW6n-t;c6({o3o&2U5F6#nbW_=c6D_gVJ=Q0RPq;3WGp()bd(qeS_O(w!r(U&Q zWYR8|;Z%J?sPcqRL0WN*M@{gF*n)J$m=k_Q<+3Hm8@($!%8%7=S|(AW);jxpb_GXG zt9r(>Z5=v=8UOwtOAZZbmgVp%@MtqH6rX&p>x+0%X-2abhs?Ymor06w@33biM&vC! z7T&)wtL{aEibt1FYS)_fQniI$I)*)GZsab%(ak?y=!%B(issq}5vm(k^j_E@a#zD~ zXGD{&N7?Jn-if`hEb^Xkbh|3`pVP>GYSA~VLi6Q{Le-T1*Vnu4oBE%b_5R+` zxm!c?|BnXdXMJ)KeL|hWAJqDJulMnl=fB@RF>+)6X^Z~F6%&G|PGG*-&w6u$+KE2y zl@q?+P-nBuf2T6x_6%kH&fbe2?JOrXHFoy>ZJqpAV^V+M}YyarkK)wzJan<|MnFYEGVoQIS*pG`r+y_UCv^ zGM3be{W&q-a%!Zcda`9nLg&Qtpy~eQ(^EAys{WrWt&yD3@o8G0M{>o@>3W_s`aOCQ zD<{87pOO(dqjtyiqMI|kKTU7zoH6a>gn-U&1I`?ylO@lW&B*2`p7pZv_zsm*8Z#$I zOqw6rwQyzT{vB%bpUs-cIb-U|y8blv?HRM@NlyK9V#*;-#b`~5H7{nL$eg}&<&;$^ z>I;9&zTi2n$#PD~;+ld>9o?0^7gx@?A~~mTrN9kOl}?M9H#sL2tekZD=iGUs9$7_g z+aqVycg((fa+2@O=&3)H13KD1tehLyIq&q$$!$mH*)M7i+%b2}$rB zTxoslc*A?oc{69u3$hgCa9DJ>nSnQI-a3n{;-B&>SEgK(T&Q_-vUZlhY&F}hnM-Vb zP7wJOwOPV?@5?3OKf4^W1e&ke9M4?(w{ywznF42T+MJ&`hkaGI*{gcjEUSl}i}P;H zy7P0P>?x!26IPEom#1gVn{soZyz&Bx$Me{t<`te?-txGGb@F`UuH~OJm-k3D*4@%! zSv5ajYQE{O`AJ6>T>7-YT4KdiuNA_L4eLGSC9Rq|yA~w=5_nr4ZnbMwbmAhFXG@=R zF3a2{aQ*wDZB@dCU5n;w&Yjk^c+IcX;RhE>eq6jGt%r4*d-kivNB>I-^=w`8>*|uq zRnuxh9e1yc50zSbnoFo>>(b9xm)?+C^(!Je*(yGjYjyRl6+bYg70$I}`f>qF zsZA!*YKOW689IHg8811WCAeC1g-n#dxm%lmEA}wF7VHjM>oQyLz2eGcodS1uZ4rOC z)R z2mMlx^Ay+XN3Y>;Y|hJG=rCiuw7Ou!#l^?DHu`0+;$vJ@n{6S%z5U&-tq&v@$%Sp6 zvU_62LNWDMoAz|A(%ZFZ-tR@a+zX#e2^vT5T*jSbUM+YjtFLeO4)u?le;ICBcU$Pf zD|s*L-8vVyhzf1pk?r^WrTpVtyR|lMWqc%3dVBYqR|^;lA`fK?W!{$id25gALMbm} zr|p-rh013u*HrJ7H+XS g&TrNGodkDf2krWBd;jv;yPiuQV7jxNk&(d~03j^*+yDRo diff --git a/doc/acknowledgments.html b/doc/acknowledgments.html index 7db7f09..4aa4072 100644 --- a/doc/acknowledgments.html +++ b/doc/acknowledgments.html @@ -20,15 +20,27 @@
Very special thanks go to Aleksey Gurtovoy, the developer of the ingenious meta programming library (boost::mpl). The interface as well as -the implementation of boost::fsm hugely benefit from Alekseys work. I would have given up long ago without mpl. -

I would also like to thank

+the implementation of boost::fsm hugely benefit from Alekseys work. I would have given up long ago without mpl. +Moreover, Aleksey's double dispatch implementation in +his FSM +framework gave me fresh ideas after I had come to the conclusion that my +dynamic_cast-based solution was too bloody bad +.

Special +thanks go to:

    -
  • David Abrahams and Gustavo Guerra for giving feedback on a very early (and quite clumsy) design of this library.
  • +
  • Peter Dimov for developing two other important libraries (lightweight_mutex, intrusive_ptr) boost::fsm + is building on.
  • +
  • various config developers whose efforts make cross-platform + development so much less painful.
  • +
+

I would also like to thank:

+
    +
  • David Abrahams and Gustavo Guerra for giving feedback on a very early (and clumsy) design of this library.
  • Aleksey Gurtovoy, Douglas Gregor and Jeff Garland for their encouragement to continue working on this library.

Revised -19 May, 2003

+30 May, 2003

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

\ No newline at end of file diff --git a/doc/index.html b/doc/index.html index fa18360..03a83e9 100644 --- a/doc/index.html +++ b/doc/index.html @@ -36,12 +36,19 @@
  • State-local storage
  • Customizable resource acquisition
  • -

    The library is not yet complete. The following is still lacking:

    -
      -
    • Support for state machines running in their own threads
    • -
    • Shallow/deep history
    • -
    • Full documentation (currently there is only a tutorial, a rationale and a FAQ page)
    • -
    +

    The library is not yet complete. Here is the current to-do list:

    +
      +
    1. The library does not yet check for invalid transitions between different + orthogonal regions, although the documentation claims otherwise.
    2. +
    3. Factor out several functions in the state_machine class + template into base classes to reduce code size in projects with many different state machines.
    4. +
    5. Add a concurrent_state_machine class template to model a state machine + running in its own thread.
    6. +
    7. Add regression tests.
    8. +
    9. Port to gcc.
    10. +
    11. Add reference documentation.
    12. +
    13. Add support for shallow/deep history.
    14. +

    This is a preliminary submission to encourage feedback. All comments are most welcome!


    Contents

    @@ -53,7 +60,7 @@

    Revised -19 May, 2003

    +30 May, 2003

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

    \ No newline at end of file diff --git a/doc/rationale.html b/doc/rationale.html index a46bc3c..7166fa8 100644 --- a/doc/rationale.html +++ b/doc/rationale.html @@ -24,12 +24,14 @@
    Why yet another state machine framework?
    State-local storage
    Dynamic configurability
    -
    Resource usage
    -
    Determinism
    Error handling
    User actions: Member functions vs. function objects
    -
    Memory management
    +
    Speed versus scalability tradeoffs
    +
    Memory management customization
    +
    RTTI customization
    Double dispatch
    +
    Resource usage
    +
    Limitations

    Introduction

    Most of the design decisions made during the development of this library are the result of the following requirements.

    @@ -52,12 +54,14 @@
  • produce a customizable reaction when a C++ exception is propagated from user code.
  • support sequential and concurrent state machines and leave it to the - end-user which thread a concurrent state machine will run in.
  • -
  • support the development of arbitrarily large and complex state machines. This means that multiple developers should be able to work on + + user which thread a concurrent state machine will run in.
  • +
  • support the development of arbitrarily large and complex state machines. Multiple developers should be able to work on the same state machine simultaneously.
  • allow the user to customize all resource management so that the library could be used for applications with hard real-time requirements.
  • enforce as much as possible at compile time. Specifically, invalid state machines should not compile.
  • +
  • offer reasonable performance for a wide range of applications.
  • Why yet another state machine framework?

    Before I started to develop this library I had a look at the following frameworks:

    @@ -76,11 +80,13 @@

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

    State-local storage

    -

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

    +

    This not yet widely known state machine feature is enabled by the fact that every state in boost::fsm +is represented by a class. Upon state-entry, an object of the class is +constructed and the object is later destructed when the state machine exits the +state. Any data that is useful only as long as the machine resides in the state +can (and should) thus be a member of the state. This feature paired with the +ability to spread a state machine over several translation units makes possible +the virtually unlimited scalability of boost::fsm. 

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

    Dynamic configurability

    Two types of state machines

      -
    • A state machine framework supports dynamic configurability if and only if the whole layout (only states and transitions, actions are - still specified with normal C++ code) of a state machine can be defined at runtime. That is, data only available at runtime can be used - to build arbitrarily large and arbitrarily complex machines. See "A Multiple Substring Search Algorithm" by Moishe Halibard and Moshe Rubin in June 2002 issue of CUJ for a good example (unfortunately not available online). Such machines almost +
    • A state machine framework supports dynamic configurability if the whole layout of a state machine can be defined at runtime + ("layout" refers to states and transitions only, actions are still specified + with normal C++ code). That is, data only available at runtime can be used + to build arbitrarily complex machines. See "A Multiple Substring Search Algorithm" by Moishe Halibard and Moshe Rubin in June 2002 issue of CUJ for a good example (unfortunately not available online). Such machines almost always get away with a simple state model (no hierarchical states, no orthogonal states, no entry and exit actions, no history) because - the state machine layout is computed by an algorithm rather than designed by a human.
    • + the machine layout is computed by an algorithm rather than designed by a human.
    • On the other side are human-designed state machines, exploiting the standard features as necessary. Naturally, the layout of such machines is known at compile time.

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

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

    +with it. 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 time. Moreover, compile time checks ensure that the @@ -116,144 +123,6 @@ It is for these reasons, that boost::fsm was built from ground up to not that it's impossible to dynamically shape a machine implemented with this library. For example, guards can be used to make different transitions depending on input only available at runtime. However, such layout changes will always be limited to what can be foreseen before compilation. A somewhat related library, the boost::spirit parser framework, allows for roughly the same runtime configurability. -

      Resource usage

      -

      Even a complex state machine should typically not use more than a few hundred -bytes of memory (assuming empty event queues). Benchmarks with the BitMachine -example compiled with MSVC7.1 (single threaded), running on a mobile AMD Athlon XP 1800, -produced the -following results:

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      Machine configuration2. Event dispatch & transition time [µs], customized memory - management3. Event dispatch & transition time [µs], normal memory - management
      2 states, 2 transitions (1 bit)1.01.8
      4 states, 8 transitions (2 bits)1.32.1
      8 states, 24 transitions (3 bits)1.52.3
      16 states, 64 transitions (4 bits)1.82.5
      32 states, 160 transitions (5 bits)2.02.8
      -

      BitMachine is not a particularly typical state machine so these -numbers give only rough guidance. -Interestingly, customization of memory management improved the times by only -30~45%. This might be different -on other platforms.

      -

      You don't pay for what you don't use

      -

      Almost:

      -
        -
      • The current interface inevitably (I believe) causes the following slight overheads for supporting some features even if they are not used:
          -
        • State-local storage: A few more function calls are made (most of which will probably be optimized away).
        • -
        • Orthogonal states: All non-leaf states (not only the orthogonal ones) need to store one more pointer and there is a very small increase in code size (one additional virtual function per executable). Bookkeeping is also slightly more complex.
        • -
        I think this will have a negligible effects on -runtime performance (memory acquisition speed is largely independent of the size -of the requested blocks as long as the blocks are so small) and memory footprint -(the size of the executable will grow much faster with increasing state machine -complexity than the memory allocated at runtime). -
      • -
      • Moreover, the current implementation has potential for optimization:
          -
        • To exit any state (no matter whether - leaf or non-leaf) one virtual function call is needed. A better - implementation could make all of the non-leaf ones non-virtual. This overhead - may become significant for highly hierarchical machines.
        • -
        • Several functions in the state_machine - class template could be factored out into non-template base classes, - reducing code size.
        • -
        -
      • -
      -

      Feature costs

      -
        -
      • State-local storage: Obviously state size will be increased by whatever state-local members the user - adds.
      • -
      • Hierarchical states: During event dispatch, the framework first tries to send the event to the innermost current state, if the - innermost state cannot consume it (because of a guard or because there is no - reaction for the event), the framework tries to send it to the - innermost states' immediate outer state. If the outer state cannot consume it either, it is sent to the immediate outer states’ outer - state and so on until an outermost state is reached. A chain of responsibility originating at the current innermost state is thus - formed.
        - The cost C (taking into account dynamic casts and virtual calls only) to dispatch one event can be formalized as follows:
        -
        - C = (d + 1) dynamic_casts + (g + 2) virtual calls
        -
        - where
        -
        - d = the “level” of the state where the transition originates seen from the leaf state. d=0 for the leaf state.
        - g = the number of “failed” guards, g <= d
        -
        - Examples: -
          -
        • The leaf state consumes the event (always true for "flat" machines) -> d = 0, g = 0 
        • -
        • The immediate outer state of the leaf state consumes -> d = 1, g = 0 or 1
        • -
        • Consider the following state configuration:
          -
          -
          -
          - E is the current state and an event EvX is currently being processed. The guard y() prevents the transition originating at D -> - d=3, g=1
        • -
        -
      • -
      • Orthogonal states:
          -
        • Each orthogonal state object needs to store one additional pointer per - orthogonal region. Code size is increased slightly (a few additional - virtual functions per executable).
        • -
        • As a state can now have an arbitrary number of orthogonal inner states, any number of innermost states can be current at a given - time. A tree rather than a chain of responsibility is thus formed. Event processing always starts with some leaf and works its way - up to the root, before it continues with the next leaf. Each visited leaf and node increases d and maybe g. The total - dispatch cost can be - calculated as outlined above.
        • -
        • If there is more than one current leaf state before a transition, one additional virtual call for each exited state is made.
        • -
        -
      • -
      • Deferring and posting events: For performance reasons and because sequential state machines often - do not need to queue events, it is possible to operate such machines entirely - with stack-allocated events. However, as soon as events need to be - deferred and/or posted there is no way around queuing and allocation with new. The - interface of simple_state::post_event enforces the use of - boost::intrusive_ptr at compile time. There is no way to do the same for - deferred events because allocation and deferral happen in completely unrelated - places. Of course, a "wrongly" allocated event could easily be transformed - into one allocated with new and pointed to by boost::intrusive_ptr with a virtual clone() function. However, in my experience, - event deferral is needed only very rarely in sequential state machines. - Moreover, fsm::concurrent_state_machine enforces the use of - boost::intrusive_ptr at compile time so most users won't run into this - limitation. I - therefore rejected the idea because of the attached code size overhead.
      • -
      -

      Determinism

      -

      For applications with hard real-time requirements it is crucial that no operation will take an indeterminable amount of time. The -following operations are potentially non-deterministic:

      -
        -
      • Memory management. All heap memory operations (operator new, operator delete, malloc, free, - etc.) are notoriously non-deterministic. As outlined below, boost::fsm allows for the customization of - all memory management.
      • -
      • dynamic_cast: Whether this operation is deterministic or not depends on your platform. On some it is, on others it is - not. However, I very much expect that compilers will use a deterministic implementation for real-time platforms.
        - If your compiler does not use a deterministic dynamic_cast implementation and you have hard - real-time requirements then boost::fsm is definitely not for you.
      • -

      Error handling

      There is not a single word about error handling in the UML state machine semantics specifications. Moreover, most existing FSM solutions @@ -278,7 +147,7 @@ action would have to post an appropriate error event and set a global error vari have to check the error variable before doing anything. After all actions have completed (by doing nothing!), the previously posted error event would have to be -processed what would lead to the remedy action begin executed. Please note that it is not sufficient to simply push the error event into the +processed what would lead to the remedy action being executed. Please note that it is not sufficient to simply push the error event into the end of the queue as other events could still be pending. Instead, the error event has absolute priority and would have to be dealt with immediately.

      So, to be safe, programmers would have to encapsulate the code of every action in if ( !error ) { /* action */ } @@ -289,7 +158,8 @@ Writing all this boiler-plate code is simply boring and quite unnecessary.

      Error handling support in boost::fsm

        -
      • C++ exceptions are used for all error handling. Except from exit-actions (mapped to state-destructors and exceptions should never be +
      • C++ exceptions are used for all error handling. Except from exit-actions (mapped to state-destructors and exceptions should + almost never be propagated from destructors), exceptions can be propagated from all user functions.
      • A customizable per state machine policy specifies how to convert all exceptions propagated from user code. Out of the box, a exception_thrown event @@ -312,21 +182,118 @@ boiler-plate code is simply boring and quite unnecessary.

      User actions: Member functions vs. function objects

      -With boost::fsm, all user-supplied functions (react functions, entry-, exit- and transition-actions) must be class members. The reasons for +With boost::fsm, all user-supplied functions (react member functions, entry-, exit- and transition-actions) must be class members. The reasons for this are as follows:
      • The concept of state-local storage mandates that state-entry and state-exit actions (mapped to constructors and destructors) are implemented as members.
      • -
      • react functions and transition actions often access state-local data. So, it is most natural to implement these functions as members of +
      • react member functions and transition actions often access state-local data. So, it is most natural to implement these functions as members of the class the data of which the functions will operate on anyway.
      -

      Memory management

      +

      Speed versus scalability tradeoffs

      +

      Quite a bit of effort has gone into making boost::fsm scaleable and +keeping it fast for small simple machines. While I believe it should perform +reasonably in most applications, the scalability does not come for free. Small, carefully handcrafted state machines will thus easily outperform equivalent boost::fsm machines. To get a picture of how big the gap +is I implemented a simple benchmark in the BitMachine example. The +Handcrafted example is a handcrafted variant of the 1-bit-BitMachine.

      +

      I tried +to create a fair but somewhat unrealistic worst-case scenario:

      +
        +
      • For both machines exactly one object of the only event is allocated before + starting the test. This same object is then sent to the machines over and + over.
      • +
      • The Handcrafted machine employs GOF-visitor double dispatch. The states + are preallocated so that event dispatch & transition amounts to nothing more + than two virtual calls and one pointer assignment.
      • +
      +

      The Benchmarks - compiled with MSVC7.1 (single threaded), running on a mobile AMD Athlon XP 1800 +- +produced the +following results:

      +
        +
      • Handcrafted: 20 nanoseconds to dispatch one event and make the resulting + transition.
      • +
      • Fully customized 1-bit-BitMachine: 250 nanoseconds to dispatch one event + and make the resulting transition.
      • +
      +

      So the boost::fsm machine is just over one order of magnitude slower than the +handcrafted machine. Although this is a big difference I still think it will +not be noticeable in most real-world applications. No matter whether an +application uses handcrafted or boost::fsm machines it will...

      + +
        +
      • almost never run into a situation where a state machine is + swamped with as many events as in the benchmarks. Unless a + state machine is abused for parsing, it will typically spend a + good deal of time waiting for events.
      • +
      • almost always allocate events with new and destroy them after consumption. + This will add quite a few cycles, even if event memory management is + customized.
      • +
      • often run state machines in their own threads. This adds event queuing and locking overhead.
      • +
      • often use state machines that employ orthogonal states and other advanced + features. This forces the handcrafted machines to use a more adequate and + more time-consuming book-keeping. 
      • +
      +

      So, in real-world applications event dispatch and transition not normally constitutes a bottleneck and the 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:

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Machine configuration
      + # states / # outgoing transitions per state
      Event dispatch & transition time [nanoseconds]
      Out of the boxCustomized memory managementCustomized memory management & RTTI
      2 / 11130330250
      4 / 21350 (reproducible anomaly!)390260
      8 / 31240460270
      16 / 41300530290
      32 / 51490650350
      64 / 61630830440
      +

      Memory management customization

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

      • There are no deterministic reaction time (hard real-time) requirements.
      • -
      • The machine will typically not process more than a few events per second. Of course, this figure depends on your platform. A typical - desktop PC could easily cope with thousands of events per second.
      • +
      • The application will typically not process more than a few events per second. Of course, this figure depends on your platform. A typical + desktop PC could easily cope with more than 50000 events per second.
      • The application will never run long enough for heap fragmentation to become a problem. This is of course an issue for all long running programs not only the ones employing boost::fsm. However, it should be noted that with this library fragmentation problems could show up earlier than with traditional FSM frameworks.
      • @@ -334,7 +301,8 @@ heap. This should be satisfactory for applications where all the following prere

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

          -
        • By passing a class offering a std::allocator interface as the last state_machine class template parameter. The rebind +
        • By passing a class offering a std::allocator interface for + the Allocator state_machine class template parameter. The rebind allocator member template is used to customize memory allocation of the internal containers.
        • By replacing the simple_state, state and event class templates with ones that have a customized @@ -350,17 +318,38 @@ that a) there is at most one object of a particular state and b) this object is always constructed, accessed and destructed by one and the same thread. We can exploit these facts in a much simpler (and faster) new/delete implementation (for example, see UniqueObject.hpp in the BitMachine example). -However, this is only possible as long as we have the freedom to overload memory management for -every single state class.

          +However, this is only possible as long as we have the freedom to customize memory management for + +state classes separately.

          +

          RTTI customization

          +

          boost::fsm uses RTTI for event dispatch and state_downcast<>(). +By default, native typeid is used but this can be changed by passing a +different class as the RttiPolicy parameter to the state_machine and event class +templates (see the BitMachine example, where RTTI is implemented with integers). +This makes event dispatch slightly faster and allows users to turn off C++ RTTI +to cut down on executable size (turning off C++ RTTI precludes usage of +state_cast<>()). By the way, I'm not particularly happy with the +interface of rtti_policy so suggestions for improvements are very welcome.

          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. Acyclic visitor was chosen for boost::fsm. The following alternatives were -considered but rejected for their bad scalability:

          +the current state define exactly which reaction the state machine will produce. +For each event dispatch, boost::fsm uses one virtual call 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 requirements but + performs badly due to costly inheritance tree cross-casts. Moreover, a state + must store one v-pointer for each reaction what 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.
          • GOF Visitor: The GOF Visitor pattern inevitably makes the whole machine depend upon all events. That is, whenever a new event is added there is no way around recompiling the whole state - machine.
          • + machine. This is contrary to the scalability requirements.
          • Two-dimensional array of function pointers: To satisfy requirement 6, it should be possible to spread a single boost::fsm state machine over several translation units. This however means, that the dispatch table must be filled at runtime and the different translation units must somehow make themselves "known", so that their part of the state machine can be added to the table. @@ -368,9 +357,64 @@ considered but rejected for their bad scalability:

            several translation units could employ table-based double dispatch lies with the user. The programmer(s) would somehow have to manually tie together the various pieces of the state machine. Not only does this scale badly but is also quite error-prone.
          +

          Resource usage

          +

          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 +than one kilobyte of memory (not counting event queues). Obviously, the +per-machine memory footprint will be offset by whatever state-local members the +user adds.

          +

          Processor cycles

          +

          The following ranking should give a rough picture of which boost::fsm +function will consume how many cycles:

          +
            +
          1. state_cast<>: By far the most cycle-consuming feature. + Searches linearly for a + suitable state, using one dynamic_cast per visited state.
          2. +
          3. State entry and exit: Profiling of the fully optimized + 1-bit-BitMachine suggested that about 100ns of the 250ns 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 + state, using one virtual call + and one RTTI comparison per visited state.
          6. +
          7. Event dispatch: One virtual call followed by a linear search for a + suitable reaction, using one RTTI comparison per visited reaction.
          8. +
          9. Orthogonal states: One additional virtual call for each exited state if + there is more than one current 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.
          10. +
          +

          Limitations

          +
            +
          • Deferring and posting events: For performance reasons and because sequential state machines often + do not need to queue events, it is possible to operate such machines entirely + with stack-allocated events. However, as soon as events need to be + deferred and/or posted there is no way around queuing and allocation with new. The + interface of simple_state::post_event enforces the use of + boost::intrusive_ptr at compile time. But there is no way to do the same for + deferred events because allocation and deferral happen in completely unrelated + places. Of course, a "wrongly" allocated event could easily be transformed + into one allocated with new and pointed to by boost::intrusive_ptr with a virtual clone() + function. However, in my experience, event deferral is needed only very rarely + in sequential state machines and the concurrent variant will enforce the use of + boost::intrusive_ptr anyway. So, most users won't run into this + limitation and I + + rejected the clone() idea because it could cause inefficiencies + casual users wouldn't be aware of. In addition, users not needing event deferral + would nevertheless pay with increased code size.
          • +

          Revised -19 May, 2003

          +30 May, 2003

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

          \ No newline at end of file diff --git a/doc/tutorial.html b/doc/tutorial.html index 0cd1914..ddc8ea8 100644 --- a/doc/tutorial.html +++ b/doc/tutorial.html @@ -27,7 +27,8 @@
          Defining states and events
          Adding reactions
          State-local storage
          -
          State queries
          +
          Getting state + information out of the machine
          A digital camera
          Spreading a state machine over multiple translation units
          Guards, junctions and choice points
          @@ -80,7 +81,8 @@ calling initiate(), which leads to the Greeting state object is destroyed what automatically exits the Greeting state.

          A few remarks:

            -
          • boost::fsm makes heavy use of the curiously recurring template pattern. The deriving class must always be passed as the first +
          • boost::fsm makes heavy use of the curiously recurring template pattern. + The deriving class must always be passed as the first parameter to the base class template.
          • The machine is not yet running after construction. We start it by calling initiate().
          • All states reside in a context. For the moment, this context is the state machine. That's why Machine is passed as the @@ -148,8 +150,7 @@ int main()
            • 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.
            • @@ -182,11 +183,11 @@ int main() myWatch.process_event( EvReset() ); return 0; } -

              Of course, a state can define an arbitrary number of reactions. That's why we have to put them into an mpl::list<> +

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

              +are also sent to the stop watch. The machine dutifully makes the transitions we would expect, but no actions are executed yet.

              State-local storage

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

                @@ -231,10 +232,13 @@ struct Running : public fsm::simple_state< Running, Active,

                Similar to when a derived class object accesses its base class portion, context<>() is used to gain access to a direct or indirect outer state object. The same function could be used to access the state machine (here context< StopWatch >()). The rest should be mostly self-explanatory. The machine now measures the time, but we cannot yet retrieve it from the main program.

                -

                State queries

                -

                Sometimes we need to be able to test whether a machine is in a particular state. That's why the state_machine class template -offers the current_state<>() function. The semantics are very similar to the ones of dynamic_cast. That is, when we call myWatch.current_state< Stopped >() and -the machine is currently in this state, we get a reference to the Stopped state. Otherwise std::bad_cast is +

                Getting state +information out of the machine

                +

                To retrieve the measured time, we need a mechanism to get state information +out of the machine. With our current machine design there are two ways to do +that. For the sake of simplicity we use the less efficient one: state_cast<>(). As the name suggests, the semantics are very similar to the ones of dynamic_cast. +For example, when we call myWatch.state_cast< const Stopped & >() and +the machine is currently in the Stopped state, we get a reference to the Stopped state. Otherwise std::bad_cast is thrown. We can use this functionality to implement a StopWatch member function that returns the elapsed time. However, rather than ask the machine in which state it is and then switch to different calculations for the elapsed time, we put the calculation into the Stopped and Running states and use an interface to retrieve the elapsed time:

                @@ -253,7 +257,7 @@ struct StopWatch : { std::clock_t ElapsedTime() const { - return current_state< IElapsedTime >().ElapsedTime(); + return state_cast< const IElapsedTime & >().ElapsedTime(); } }; @@ -308,13 +312,16 @@ extends this program to an interactive console application.

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

                • Bad scalability: As soon as the compiler reaches the point where state_machine::initiate() is called, a number of - template instantiations take place which can only succeed if the full declaration of each and every state of the machine is known. That + template instantiations take place, which can only succeed if the full declaration of each and every state of the machine is known. That is, the whole layout of a state machine must be implemented in one single translation unit (actions can be compiled separately, but this is of no importance here). For bigger (and more real-world) state machines, this leads to the following limitations:
                  • At some point compilers reach their internal template instantiation limits and give up. This can happen even for moderately-sized - machines. For example, in debug mode one popular compiler refuses to compile the BitMachine example for anything above 3 bits. This - means that the compiler reaches its limits somewhere between 8 states, 24 transitions and 16 states, 64 transitions.
                  • + machines. For example, in debug mode one popular compiler refused to + compile earlier versions of the BitMachine example for anything above + 3 bits. This + means that the compiler reached its limits somewhere between 8 states, + 24 transitions and 16 states, 64 transitions.
                  • Multiple programmers can hardly work on the same state machine simultaneously because every layout change will inevitably lead to a recompilation of the whole state machine.
                  • @@ -334,9 +341,9 @@ them!

                    Spreading a state machine over multiple translation units

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

                      -
                    • Shutter button, which can be half-pressed and fully-pressed. The associated events are EvShutterHalf, EvShutterFull and - EvShutterReleased
                    • -
                    • Config button, represented by the EvConfig event
                    • +
                    • Shutter button, which can be half-pressed and fully-pressed. The associated events are EvShutterHalf, EvShutterFull and + EvShutterReleased
                    • +
                    • Config button, represented by the EvConfig event
                    • A number of other buttons that are not of interest here

                    One use case for the camera says that the photographer can half-press the shutter anywhere in the configuration mode and the @@ -345,7 +352,8 @@ camera will immediately go into shooting mode. The following state chart is one

                    The Configuring and Shooting states will contain numerous nested states while the Idle state is relatively simple. It was therefore decided to build two teams. One will implement the shooting mode while the other will implement the configuration mode. The two teams have already agreed on the interface that the shooting team will use to retrieve the configuration settings. We would like to ensure that the two -teams can work with the least possible interference. So, we put the two states in their own translation units to ensure that machine layout +teams can work with the least possible interference. So, we put the two states in their own translation units +so that machine layout changes within the Configuring state will never lead to a recompilation of the inner workings of the Shooting state and vice versa.

                    Unlike in the previous example, the excerpts presented here often outline different options to achieve the same effect. That's why the @@ -381,19 +389,19 @@ struct NotShooting : fsm::custom_reaction< EvShutterHalf >, Idle > { // ... - virtual fsm::result react( const EvShutterHalf & ); + fsm::result react( const EvShutterHalf & ); }; struct Idle : public fsm::simple_state< Idle, NotShooting, fsm::custom_reaction< EvConfig > > { // ... - virtual fsm::result react( const EvConfig & ); + fsm::result react( const EvConfig & ); }; #endif

                    Please note the bold parts in the code. With a custom reaction we only specify that we might do something with a particular -event, but the actual reaction is defined in the react() member function, which can be implemented in the .cpp file.

                    +event, but the actual reaction is defined in the react member function, which can be implemented in the .cpp file.

                    Camera.cpp:

                    #include "Camera.hpp"
                     #include "Configuring.hpp"
                    @@ -413,7 +421,8 @@ fsm::result Idle::react( const EvConfig & )
                     }

                    Caution: Any call to the simple_state::transit<>() or simple_state::terminate() (see later) member functions will inevitably destruct the current state object (similar to delete this;)! That is, -reaction code + +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.

                    @@ -448,8 +457,8 @@ fsm::result NotShooting::react( const EvShutterHalf & ) if ( context< Camera >().IsBatteryLow() ) { // We cannot react to the event ourselves, so we forward it - // to our outer state (this is also the default if there - // is no reaction for a given event). + // to our outer state (this is also the default if a state + // defines no reaction for a given event). return forward_event(); } else @@ -520,7 +529,7 @@ struct Camera : struct Focusing : public fsm::simple_state< Focusing, Shooting, fsm::transition< EvInFocus, Focused, Camera, &Camera::DisplayFocused > > {}; -

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

                    +

                    Naturally, transition actions can also be invoked from custom reactions:

                    Shooting.cpp:

                    // ...
                     fsm::result Focusing::react( const EvInFocus & evt )
                    @@ -529,15 +538,16 @@ fsm::result Focusing::react( const EvInFocus & evt )
                     }

                    Please note that we have to manually forward the event.

                    Advanced topics

                    -

                    Reaction functions

                    -

                    The following functions can only be called from within react overrides. The override must +

                    Reaction function reference

                    +

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

                    • simple_state::forward_event(): The dispatch algorithm keeps searching for a reaction for the current event. The search always continues with the immediate outer state. If there is none it continues with the next orthogonal leaf state. This process is repeated until one of the visited states returns by calling any of the other 5 reaction functions. The event is - silently discarded if no reaction was found.
                      + silently discarded if no reaction can be found.
                      forward_event() is also the default for all states that do not define a reaction for the event.
                    • simple_state::discard_event(): The dispatch algorithm stops searching for @@ -557,30 +567,26 @@ return by calling exactly one function (e.g. return terminate();simple_state::terminate(): Terminates the state and returns by calling simple_state::discard_event().
                    -

                    Reactions

                    +

                    Reaction reference

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

                      -
                    • transition< Event, DestinationState >: defines a - custom_reaction with a react override that returns - transit< DestinationState >();
                    • -
                    • transition< Event, DestinationState, TransitionContext, void ( TransitionContext::*pTransitionAction )( const Event & ) >: - defines a custom_reaction with a react override that returns - transit< DestinationState >( pTransitionAction, evt );
                    • -
                    • termination< Event >: defines a custom_reaction - with a - react override that returns terminate();
                    • -
                    • deferal< Event >: defines a custom_reaction - with a react - override that returns defer_event();. Please see +
                    • transition< Event, DestinationState >: returns + simple_state::transit< DestinationState >();
                    • +
                    • transition< Event, DestinationState, TransitionContext, void ( TransitionContext::*pTransitionAction )( const Event & ) >: returns + simple_state::transit< DestinationState >( pTransitionAction, evt );
                    • +
                    • termination< Event >: returns simple_state::terminate();
                    • +
                    • deferral< Event >: returns simple_state::defer_event();. Please see Deferring events!
                    • custom_reaction< Event >: returns react( evt - ); (the user-supplied override of the pure virtual function). The react override must return + ); (the user-supplied member function). The react member + function must return by calling one of the reaction functions.
                    -

                    Should a user find herself implementing similar react overrides very often, +

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

                    Specifying multiple reactions @@ -618,30 +624,29 @@ for a state

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

                    • simple_state::context<>()
                    • simple_state::post_event()
                    • +
                    • simple_state::state_cast<>()
                    • +
                    • simple_state::state_downcast<>()

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

                    Deferring events

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

                    class Event : public fsm::event< Event > {};
                     struct Initial;
                     struct Machine : public fsm::state_machine<
                       Machine, Initial > {};
                     struct Initial : public fsm::simple_state< Initial, Machine,
                    -  fsm::deferal< Event > > {};
                    +  fsm::deferral< Event > > {};
                     
                     int main()
                     {
                    @@ -724,8 +729,8 @@ actions (mapped to destructors and destructors should virtually never throw in C
                     of the box, state_machine does the following:

                    1. The exception is caught.
                    2. -
                    3. In the catch block, an exception_thrown event is allocated on the stack.
                    4. -
                    5. Also in the catch block, an immediate dispatch of the exception_thrown +
                    6. In the catch block, an fsm::exception_thrown event is allocated on the stack.
                    7. +
                    8. Also in the catch block, an immediate dispatch of the fsm::exception_thrown event is attempted. That is, possibly remaining events in the queue are dispatched only after the exception has been handled successfully.
                    9. If the exception was handled successfully, the state machine returns to @@ -733,14 +738,14 @@ of the box, state_machine does the following:

                      so that the client of the state machine can handle the exception.

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

                    -

                    Which states can react to an exception_thrown event?

                    +

                    Which states can react to an fsm::exception_thrown event?

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

                      -
                    1. The react override of a custom reaction propagates an +
                    2. A react member function propagates an exception before calling any of the reaction functions. The state that caused the exception is first tried for a reaction, so the following machine will transit to Defective after receiving an EvStart event:
                      @@ -760,7 +765,8 @@ work around buggy exception handling implementations (see

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

                    @@ -770,7 +776,7 @@ Defective after receiving an EvNumLockPressed event:

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

                    Successful exception handling

                    An exception is considered handled successfully, if:
                      -
                    • an appropriate reaction to the exception_thrown event not returning by calling forward_event() has +
                    • an appropriate reaction to the fsm::exception_thrown event not returning by calling forward_event() has been found.
                    • the state machine is in a stable state, after the reaction has completed.
                    • @@ -781,9 +787,9 @@ exception is handled. The machine would be left in an invalid state, should the reaction simply discard the event without doing anything else.

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

                      +propagated to the machine client.

                      Discriminating exceptions

                      -

                      Because the exception_event object is dispatched from within the catch block, +

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

                      struct Defective : public fsm::simple_state<
                         Defective, Purifier > {};
                      @@ -791,14 +797,14 @@ we can rethrow and catch the exception in a custom reaction:

                      struct Idle : public fsm::simple_state< Idle, Purifier, mpl::list< fsm::custom_reaction< EvStart >, - fsm::custom_reaction< exception_thrown > > > + fsm::custom_reaction< fsm::exception_thrown > > > { - virtual fsm::result react( const EvStart & ) + fsm::result react( const EvStart & ) { - throw std::runtime_error(); + throw std::runtime_error( "" ); } - virtual fsm::result react( const exception_thrown & ) + fsm::result react( const fsm::exception_thrown & ) { try { @@ -807,8 +813,8 @@ struct Idle : public fsm::simple_state< Idle, Purifier, catch ( const std::runtime_error & ) { // only std::runtime_errors will lead to a transition - // Defective, all other exceptions are propagated to - // the state machine client + // to Defective, all other exceptions are propagated + // to the state machine client return transit< Defective >(); } } @@ -851,10 +857,10 @@ template< LockType lockType > struct Off : public fsm::simple_state< Off< lockType >, Active::orthogonal< lockType >, fsm::transition< Pressed< lockType >, On< lockType > > > {};
                      -

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

                      +

                      The On and Off templates could be given additional parameters to make them truly reusable.


                      Revised -19 May, 2003

                      +30 May, 2003

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

                      \ No newline at end of file diff --git a/example/BitMachine/BitMachine.cpp b/example/BitMachine/BitMachine.cpp index 9d59f91..ed9976e 100644 --- a/example/BitMachine/BitMachine.cpp +++ b/example/BitMachine/BitMachine.cpp @@ -77,6 +77,8 @@ const unsigned int noOfBits = 6; #include #include "UniqueObject.hpp" +#include "IntegerRttiPolicy.hpp" + namespace fsm = boost::fsm; @@ -88,7 +90,8 @@ using namespace mpl::placeholders; const unsigned int noOfStates = 1 << noOfBits; const unsigned int noOfTransitions = noOfStates * noOfBits; -const unsigned int noOfEvents = 3 * 3 * 5 * 7 * 31 * 127; +// common prime factors of 2^n-1 for n in [1,8] +const unsigned int noOfEvents = 3 * 3 * 5 * 7 * 17 * 31 * 127; const unsigned int noOfLaps = noOfEvents / ( noOfStates - 1 ); unsigned long eventsSentTotal = 0; @@ -113,26 +116,20 @@ void DisplayBits( unsigned int number ) ////////////////////////////////////////////////////////////////////////////// template< unsigned int bitNo > -class EvFlipBit : public fsm::event {}; -const EvFlipBit< 0 > flip0; -const EvFlipBit< 1 > flip1; -const EvFlipBit< 2 > flip2; -const EvFlipBit< 3 > flip3; -const EvFlipBit< 4 > flip4; -const EvFlipBit< 5 > flip5; -const EvFlipBit< 6 > flip6; -const EvFlipBit< 7 > flip7; -const EvFlipBit< 8 > flip8; -const EvFlipBit< 9 > flip9; -const fsm::event * const pFlipBitEvents[] = - { &flip0, &flip1, &flip2, &flip3, &flip4, &flip5, &flip6, &flip7, &flip8, &flip9 }; +class EvFlipBit : + public fsm::event< EvFlipBit< bitNo >, IntegerRttiPolicy<> > {}; +const fsm::event_base< IntegerRttiPolicy<> > * pFlipBitEvents[ 10 ] = { 0 }; + +class ExceptionThrown : + public fsm::event< ExceptionThrown, IntegerRttiPolicy<> > {}; template< unsigned int stateNo > class BitState; ////////////////////////////////////////////////////////////////////////////// class BitMachine : public fsm::state_machine< BitMachine, BitState< 0 >, - fsm::exception_translator, boost::fast_pool_allocator< int > > {}; + boost::fast_pool_allocator< int >, + fsm::exception_translator< ExceptionThrown >, IntegerRttiPolicy<> > {}; ////////////////////////////////////////////////////////////////////////////// @@ -180,7 +177,8 @@ class IDisplay ////////////////////////////////////////////////////////////////////////////// template< unsigned int stateNo > class BitState : public fsm::simple_state< - BitState< stateNo >, BitMachine, typename FlipTransitionList< stateNo >::type >, + BitState< stateNo >, BitMachine, + typename FlipTransitionList< stateNo >::type >, public IDisplay, public UniqueObject< BitState< stateNo > > { public: @@ -247,6 +245,28 @@ int main( int argc, char * argv[] ) BOOST_ASSERT( noOfBits <= 10 ); + const EvFlipBit< 0 > flip0; + const EvFlipBit< 1 > flip1; + const EvFlipBit< 2 > flip2; + const EvFlipBit< 3 > flip3; + const EvFlipBit< 4 > flip4; + const EvFlipBit< 5 > flip5; + const EvFlipBit< 6 > flip6; + const EvFlipBit< 7 > flip7; + const EvFlipBit< 8 > flip8; + const EvFlipBit< 9 > flip9; + + pFlipBitEvents[ 0 ] = &flip0; + pFlipBitEvents[ 1 ] = &flip1; + pFlipBitEvents[ 2 ] = &flip2; + pFlipBitEvents[ 3 ] = &flip3; + pFlipBitEvents[ 4 ] = &flip4; + pFlipBitEvents[ 5 ] = &flip5; + pFlipBitEvents[ 6 ] = &flip6; + pFlipBitEvents[ 7 ] = &flip7; + pFlipBitEvents[ 8 ] = &flip8; + pFlipBitEvents[ 9 ] = &flip9; + std::cout << "boost::fsm BitMachine example\n"; std::cout << "Machine configuration: " << noOfStates << " states interconnected with " << noOfTransitions << " transitions.\n\n"; diff --git a/example/BitMachine/BitMachine.vcproj b/example/BitMachine/BitMachine.vcproj index 9797afe..1cb34d3 100644 --- a/example/BitMachine/BitMachine.vcproj +++ b/example/BitMachine/BitMachine.vcproj @@ -28,7 +28,7 @@ SmallerTypeCheck="TRUE" RuntimeLibrary="5" BufferSecurityCheck="TRUE" - DisableLanguageExtensions="FALSE" + DisableLanguageExtensions="TRUE" TreatWChar_tAsBuiltInType="TRUE" ForceConformanceInForLoopScope="TRUE" RuntimeTypeInfo="TRUE" @@ -88,8 +88,9 @@ StringPooling="TRUE" SmallerTypeCheck="FALSE" RuntimeLibrary="4" + BufferSecurityCheck="FALSE" EnableFunctionLevelLinking="TRUE" - DisableLanguageExtensions="FALSE" + DisableLanguageExtensions="TRUE" TreatWChar_tAsBuiltInType="TRUE" ForceConformanceInForLoopScope="TRUE" RuntimeTypeInfo="TRUE" @@ -159,6 +160,9 @@ + + @@ -178,6 +182,9 @@ + + diff --git a/example/BitMachine/UniqueObjectAllocator.hpp b/example/BitMachine/UniqueObjectAllocator.hpp index b51eac3..7392943 100644 --- a/example/BitMachine/UniqueObjectAllocator.hpp +++ b/example/BitMachine/UniqueObjectAllocator.hpp @@ -56,7 +56,8 @@ class UniqueObjectAllocator template< class T > bool UniqueObjectAllocator< T >::constructed_ = false; template< class T > -typename UniqueObjectAllocator< T >::storage UniqueObjectAllocator< T >::storage_; +typename UniqueObjectAllocator< T >::storage + UniqueObjectAllocator< T >::storage_; diff --git a/example/Camera/Camera.hpp b/example/Camera/Camera.hpp index 759317e..eea03c1 100644 --- a/example/Camera/Camera.hpp +++ b/example/Camera/Camera.hpp @@ -19,10 +19,10 @@ namespace fsm = boost::fsm; -class EvShutterHalf : public fsm::event {}; -class EvShutterFull : public fsm::event {}; -class EvShutterRelease : public fsm::event {}; -class EvConfig : public fsm::event {}; +class EvShutterHalf : public fsm::event< EvShutterHalf > {}; +class EvShutterFull : public fsm::event< EvShutterFull > {}; +class EvShutterRelease : public fsm::event< EvShutterRelease > {}; +class EvConfig : public fsm::event< EvConfig > {}; struct NotShooting; diff --git a/example/Camera/Camera.vcproj b/example/Camera/Camera.vcproj index 3edb262..56670f9 100644 --- a/example/Camera/Camera.vcproj +++ b/example/Camera/Camera.vcproj @@ -201,6 +201,9 @@ + + diff --git a/example/Camera/Shooting.hpp b/example/Camera/Shooting.hpp index 695c6b2..097b1eb 100644 --- a/example/Camera/Shooting.hpp +++ b/example/Camera/Shooting.hpp @@ -24,7 +24,7 @@ namespace mpl = boost::mpl; -struct EvInFocus : public fsm::event {}; +struct EvInFocus : public fsm::event< EvInFocus > {}; struct Focusing; struct Shooting : public fsm::simple_state< Shooting, Camera, diff --git a/example/Handcrafted/Handcrafted.cpp b/example/Handcrafted/Handcrafted.cpp index d4a9ec1..da25c9e 100644 --- a/example/Handcrafted/Handcrafted.cpp +++ b/example/Handcrafted/Handcrafted.cpp @@ -25,7 +25,8 @@ const unsigned int noOfStates = 2; const unsigned int noOfTransitions = 2; -const unsigned int noOfEvents = 3 * 3 * 5 * 7 * 31 * 127; +// common prime factors of 2^n-1 for n in [1,8] +const unsigned int noOfEvents = 3 * 3 * 5 * 7 * 17 * 31 * 127; unsigned long eventsSentTotal = 0; diff --git a/example/Handcrafted/Handcrafted.vcproj b/example/Handcrafted/Handcrafted.vcproj index 6946e11..d7a0b2d 100644 --- a/example/Handcrafted/Handcrafted.vcproj +++ b/example/Handcrafted/Handcrafted.vcproj @@ -88,8 +88,9 @@ StringPooling="TRUE" SmallerTypeCheck="FALSE" RuntimeLibrary="4" + BufferSecurityCheck="FALSE" EnableFunctionLevelLinking="TRUE" - DisableLanguageExtensions="FALSE" + DisableLanguageExtensions="TRUE" TreatWChar_tAsBuiltInType="TRUE" ForceConformanceInForLoopScope="TRUE" RuntimeTypeInfo="TRUE" diff --git a/example/StateSize/StateSize.cpp b/example/StateSize/StateSize.cpp index 8de6c8b..c450069 100644 --- a/example/StateSize/StateSize.cpp +++ b/example/StateSize/StateSize.cpp @@ -9,6 +9,7 @@ #include +#include #include #include @@ -27,10 +28,10 @@ namespace mpl = boost::mpl; ////////////////////////////////////////////////////////////////////////////// // Displays the runtime sizes of states ////////////////////////////////////////////////////////////////////////////// -class DummyEvent1 : public fsm::event {}; -class DummyEvent2 : public fsm::event {}; -class DummyEvent3 : public fsm::event {}; -class DummyEvent4 : public fsm::event {}; +class DummyEvent1 : public fsm::event< DummyEvent1 > {}; +class DummyEvent2 : public fsm::event< DummyEvent2 > {}; +class DummyEvent3 : public fsm::event< DummyEvent3 > {}; +class DummyEvent4 : public fsm::event< DummyEvent4 > {}; class UnconnectedOuterState; @@ -83,23 +84,23 @@ int main( int argc, char * argv[] ) "boost::fsm::state sizes\n\n" << std::setw( 50 ) << "detail::counted_base< unsigned char, false >: " << sizeof( fsm::detail::counted_base< unsigned char, false > ) << "\n" << - std::setw( 50 ) << "detail::state_base: " << - sizeof( fsm::detail::state_base ) << "\n" << - std::setw( 50 ) << "detail::universal_state< std::list< _ > >: " << + std::setw( 50 ) << "detail::state_base< ... >: " << + sizeof( fsm::detail::state_base< fsm::rtti_policy > ) << "\n" << + std::setw( 50 ) << "detail::universal_state< ... >: " << sizeof( fsm::detail::universal_state< - std::list< boost::intrusive_ptr< fsm::detail::state_base > > > ) << "\n" << - std::setw( 50 ) << "detail::leaf_state< std::list< _ > >: " << + std::list< boost::intrusive_ptr< fsm::detail::state_base< fsm::rtti_policy > > >, fsm::rtti_policy > ) << "\n" << + std::setw( 50 ) << "detail::leaf_state< ... >: " << sizeof( fsm::detail::leaf_state< - std::list< boost::intrusive_ptr< fsm::detail::state_base > > > ) << "\n" << - std::setw( 50 ) << "detail::node_state< 1, std::list< _ > >: " << + std::list< boost::intrusive_ptr< fsm::detail::state_base< fsm::rtti_policy > > >, fsm::rtti_policy > ) << "\n" << + std::setw( 50 ) << "detail::node_state< 1, ... >: " << sizeof( fsm::detail::node_state< 1, - std::list< boost::intrusive_ptr< fsm::detail::state_base > > > ) << "\n" << - std::setw( 50 ) << "detail::node_state< 2, std::list< _ > >: " << + std::list< boost::intrusive_ptr< fsm::detail::state_base< fsm::rtti_policy > > >, fsm::rtti_policy > ) << "\n" << + std::setw( 50 ) << "detail::node_state< 2, ... >: " << sizeof( fsm::detail::node_state< 2, - std::list< boost::intrusive_ptr< fsm::detail::state_base > > > ) << "\n" << - std::setw( 50 ) << "detail::node_state< 3, std::list< _ > >: " << + std::list< boost::intrusive_ptr< fsm::detail::state_base< fsm::rtti_policy > > >, fsm::rtti_policy > ) << "\n" << + std::setw( 50 ) << "detail::node_state< 3, ... >: " << sizeof( fsm::detail::node_state< 3, - std::list< boost::intrusive_ptr< fsm::detail::state_base > > > ) << "\n\n" << + std::list< boost::intrusive_ptr< fsm::detail::state_base< fsm::rtti_policy > > >, fsm::rtti_policy > ) << "\n\n" << std::setw( 50 ) << "simple_state< _, _, no_transitions, _ >: " << sizeof( UnconnectedOuterState ) << "\n" << diff --git a/example/StateSize/StateSize.vcproj b/example/StateSize/StateSize.vcproj index b505902..7831616 100644 --- a/example/StateSize/StateSize.vcproj +++ b/example/StateSize/StateSize.vcproj @@ -157,6 +157,9 @@ + + diff --git a/example/StopWatch/StopWatch.cpp b/example/StopWatch/StopWatch.cpp index 313e954..c9e2957 100644 --- a/example/StopWatch/StopWatch.cpp +++ b/example/StopWatch/StopWatch.cpp @@ -48,8 +48,8 @@ namespace mpl = boost::mpl; -class EvStartStop : public fsm::event {}; -class EvReset : public fsm::event {}; +class EvStartStop : public fsm::event< EvStartStop > {}; +class EvReset : public fsm::event< EvReset > {}; struct IElapsedTime { diff --git a/example/StopWatch/StopWatch.vcproj b/example/StopWatch/StopWatch.vcproj index e37e3bf..78d7aae 100644 --- a/example/StopWatch/StopWatch.vcproj +++ b/example/StopWatch/StopWatch.vcproj @@ -27,7 +27,7 @@ SmallerTypeCheck="TRUE" RuntimeLibrary="1" BufferSecurityCheck="TRUE" - DisableLanguageExtensions="FALSE" + DisableLanguageExtensions="TRUE" TreatWChar_tAsBuiltInType="TRUE" ForceConformanceInForLoopScope="TRUE" RuntimeTypeInfo="TRUE" @@ -87,7 +87,7 @@ SmallerTypeCheck="FALSE" RuntimeLibrary="0" EnableFunctionLevelLinking="TRUE" - DisableLanguageExtensions="FALSE" + DisableLanguageExtensions="TRUE" TreatWChar_tAsBuiltInType="TRUE" ForceConformanceInForLoopScope="TRUE" RuntimeTypeInfo="TRUE" @@ -157,6 +157,9 @@ + + diff --git a/include/boost/statechart/custom_reaction.hpp b/include/boost/statechart/custom_reaction.hpp index 83e95f1..59b6db0 100644 --- a/include/boost/statechart/custom_reaction.hpp +++ b/include/boost/statechart/custom_reaction.hpp @@ -10,13 +10,10 @@ -#include #include #include // boost::polymorphic_downcast -#include // std::type_info - namespace boost @@ -30,11 +27,11 @@ namespace fsm template< class Event > struct custom_reaction { - template< class State > + template< class State, class EventBase, class IdType > static result react( - State & stt, const event & evt, const std::type_info & eventType ) + State & stt, const EventBase & evt, const IdType & eventType ) { - if ( eventType == typeid( const Event ) ) + if ( eventType == Event::static_type() ) { return stt.react( *polymorphic_downcast< const Event * >( &evt ) ); } diff --git a/include/boost/statechart/deferal.hpp b/include/boost/statechart/deferal.hpp index b322f69..2761baa 100644 --- a/include/boost/statechart/deferal.hpp +++ b/include/boost/statechart/deferal.hpp @@ -10,11 +10,8 @@ -#include #include -#include // std::type_info - namespace boost @@ -28,11 +25,11 @@ namespace fsm template< class Event > struct deferal { - template< class State > + template< class State, class EventBase, class IdType > static result react( - State & stt, const event &, const std::type_info & eventType ) + State & stt, const EventBase &, const IdType & eventType ) { - if ( eventType == typeid( const Event ) ) + if ( eventType == Event::static_type() ) { return stt.defer_event(); } diff --git a/include/boost/statechart/detail/counted_base.hpp b/include/boost/statechart/detail/counted_base.hpp index 2b61f8a..243adef 100644 --- a/include/boost/statechart/detail/counted_base.hpp +++ b/include/boost/statechart/detail/counted_base.hpp @@ -64,7 +64,7 @@ class counted_base : private locked_base< NeedsLocking > public: ////////////////////////////////////////////////////////////////////////// - // CAUTION: The following declarations should be private. + // The following declarations should be private. // They are only public because many compilers lack template friends. ////////////////////////////////////////////////////////////////////////// void add_ref() const diff --git a/include/boost/statechart/detail/leaf_state.hpp b/include/boost/statechart/detail/leaf_state.hpp index 1069a8c..057efeb 100644 --- a/include/boost/statechart/detail/leaf_state.hpp +++ b/include/boost/statechart/detail/leaf_state.hpp @@ -24,19 +24,17 @@ namespace detail ////////////////////////////////////////////////////////////////////////////// -template< class StateList > -class leaf_state : public universal_state< StateList > +template< class StateList, class RttiPolicy > +class leaf_state : public universal_state< StateList, RttiPolicy > { - typedef universal_state< StateList > base_type; + typedef universal_state< StateList, RttiPolicy > base_type; protected: ////////////////////////////////////////////////////////////////////////// - leaf_state() {} - - using base_type::set_context; + leaf_state( typename RttiPolicy::id_type id ) : base_type( id ) {} public: ////////////////////////////////////////////////////////////////////////// - // CAUTION: The following declarations should be private. + // The following declarations should be private. // They are only public because many compilers lack template friends. ////////////////////////////////////////////////////////////////////////// void set_list_position( typename StateList::iterator listPosition ) diff --git a/include/boost/statechart/detail/node_state.hpp b/include/boost/statechart/detail/node_state.hpp index f7dcbec..7452a02 100644 --- a/include/boost/statechart/detail/node_state.hpp +++ b/include/boost/statechart/detail/node_state.hpp @@ -26,29 +26,29 @@ namespace detail ////////////////////////////////////////////////////////////////////////////// -template< orthogonal_position_type noOfOrthogonalRegions, class StateList > -class node_state : public universal_state< StateList > +template< orthogonal_position_type noOfOrthogonalRegions, + class StateList, class RttiPolicy > +class node_state : public universal_state< StateList, RttiPolicy > { - typedef universal_state< StateList > base_type; + typedef universal_state< StateList, RttiPolicy > base_type; protected: ////////////////////////////////////////////////////////////////////////// - node_state() + node_state( typename RttiPolicy::id_type id ) : base_type( id ) { - for ( orthogonal_position_type pos = 0; pos < noOfOrthogonalRegions; ++pos ) + for ( orthogonal_position_type pos = 0; + pos < noOfOrthogonalRegions; ++pos ) { pInnerStates[ pos ] = 0; } } - using base_type::set_context; - public: ////////////////////////////////////////////////////////////////////////// - // CAUTION: The following declarations should be private. + // The following declarations should be private. // They are only public because many compilers lack template friends. ////////////////////////////////////////////////////////////////////////// void add_inner_state( orthogonal_position_type position, - universal_state< StateList > * pInnerState ) + base_type * pInnerState ) { BOOST_ASSERT( ( position < noOfOrthogonalRegions ) && ( pInnerStates[ position ] == 0 ) ); @@ -67,7 +67,7 @@ class node_state : public universal_state< StateList > if ( ( pUnstableState != states.end() ) && ( get_pointer( *pUnstableState ) == this ) ) { - for ( universal_state< StateList > ** pState = &pInnerStates[ 0 ]; + for ( base_type ** pState = &pInnerStates[ 0 ]; pState != &pInnerStates[ noOfOrthogonalRegions ]; ++pState ) { BOOST_ASSERT( *pState == 0 ); @@ -79,16 +79,15 @@ class node_state : public universal_state< StateList > else { // Destroy inner states in the reverse order of construction - for ( universal_state< StateList > ** pState = - &pInnerStates[ noOfOrthogonalRegions ]; - pState != &pInnerStates[ 0 ]; --pState ) + for ( base_type ** pState = &pInnerStates[ noOfOrthogonalRegions ]; + pState != &pInnerStates[ 0 ]; ) { + --pState; // An inner orthogonal state might have been terminated long before, // that's why we have to check for 0 pointers - if ( *( pState - 1 ) != 0 ) + if ( *pState != 0 ) { - ( *( pState - 1 ) )->remove_from_state_list( - states, pUnstableState ); + ( *pState )->remove_from_state_list( states, pUnstableState ); } } } @@ -96,7 +95,7 @@ class node_state : public universal_state< StateList > private: ////////////////////////////////////////////////////////////////////////// - universal_state< StateList > * pInnerStates[ noOfOrthogonalRegions ]; + base_type * pInnerStates[ noOfOrthogonalRegions ]; }; diff --git a/include/boost/statechart/detail/state_base.hpp b/include/boost/statechart/detail/state_base.hpp index 398f5b6..30f5642 100644 --- a/include/boost/statechart/detail/state_base.hpp +++ b/include/boost/statechart/detail/state_base.hpp @@ -11,12 +11,12 @@ #include +#include + #include -#include // boost::noncopyable -#include // BOOST_ASSERT - -#include // std::type_info +#include +#include @@ -36,10 +36,6 @@ namespace fsm -class event; - - - namespace detail { @@ -50,23 +46,21 @@ typedef unsigned char orthogonal_position_type; ////////////////////////////////////////////////////////////////////////////// +template< class RttiPolicy > class state_base : private noncopyable, // Derived class objects will be created, handled and destroyed by one and // the same thread --> locking is not necessary - public counted_base< orthogonal_position_type, false > + public RttiPolicy::base_type< counted_base< orthogonal_position_type, false > > { - public: - ////////////////////////////////////////////////////////////////////////// - virtual result react_impl( - const event & evt, const std::type_info & 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 typename RttiPolicy::base_type< + counted_base< orthogonal_position_type, false > > base_type; protected: ////////////////////////////////////////////////////////////////////////// - state_base() : + // 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 ), reactionEnabled_( false ), deferredEvents_( false ) { @@ -99,6 +93,19 @@ class state_base : private noncopyable, return deferredEvents_; } + public: + ////////////////////////////////////////////////////////////////////////// + // The following declarations should be private. + // They are only public because many compilers lack template friends. + ////////////////////////////////////////////////////////////////////////// + virtual result react_impl( + const event_base< RttiPolicy > & 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; + private: ////////////////////////////////////////////////////////////////////////// bool reactionEnabled_; diff --git a/include/boost/statechart/detail/universal_state.hpp b/include/boost/statechart/detail/universal_state.hpp index ae7932b..958b800 100644 --- a/include/boost/statechart/detail/universal_state.hpp +++ b/include/boost/statechart/detail/universal_state.hpp @@ -24,13 +24,16 @@ namespace detail ////////////////////////////////////////////////////////////////////////////// -template< class StateList > -class universal_state : public state_base +template< class StateList, class RttiPolicy > +class universal_state : public state_base< RttiPolicy > { - typedef state_base base_type; + typedef state_base< RttiPolicy > base_type; protected: ////////////////////////////////////////////////////////////////////////// - universal_state() {} + // The following declarations should be private. + // They are only protected because many compilers lack template friends. + ////////////////////////////////////////////////////////////////////////// + universal_state( typename RttiPolicy::id_type id ) : base_type( id ) {} template< class Context > void set_context( orthogonal_position_type position, Context * pContext ) @@ -40,7 +43,7 @@ class universal_state : public state_base public: ////////////////////////////////////////////////////////////////////////// - // CAUTION: The following declarations should be private. + // The following declarations should be private. // They are only public because many compilers lack template friends. ////////////////////////////////////////////////////////////////////////// virtual void remove_from_state_list( diff --git a/include/boost/statechart/event.hpp b/include/boost/statechart/event.hpp index 59ee75b..74dc257 100644 --- a/include/boost/statechart/event.hpp +++ b/include/boost/statechart/event.hpp @@ -12,11 +12,20 @@ #include +#include + #include #include #include -#include + + +#ifdef BOOST_MSVC +// We permanently turn off the following level 4 warnings because users will +// have to do so themselves anyway if we turn them back on +#pragma warning( disable: 4511 ) // copy constructor could not be generated +#pragma warning( disable: 4512 ) // assignment operator could not be generated +#endif @@ -28,16 +37,31 @@ namespace fsm ////////////////////////////////////////////////////////////////////////////// -class event : private noncopyable, public detail::counted_base< unsigned int > +template< class RttiPolicy > +class event_base : private noncopyable, + public RttiPolicy::base_type< detail::counted_base< unsigned int > > { + typedef typename RttiPolicy::base_type< + detail::counted_base< unsigned int > > base_type; public: ////////////////////////////////////////////////////////////////////////// - intrusive_ptr< const event > clone() const + intrusive_ptr< const event_base > clone() const { - BOOST_ASSERT( ref_counted() ); - return intrusive_ptr< const event >( this ); + BOOST_ASSERT( base_type::ref_counted() ); + return intrusive_ptr< const event_base >( this ); } + protected: + ////////////////////////////////////////////////////////////////////////// + event_base( typename RttiPolicy::id_type id ) : base_type( id ) {} +}; + + +////////////////////////////////////////////////////////////////////////////// +template< class MostDerived, class RttiPolicy = rtti_policy > +class event : public RttiPolicy::derived_type< + MostDerived, event_base< RttiPolicy > > +{ protected: ////////////////////////////////////////////////////////////////////////// event() {} diff --git a/include/boost/statechart/exception_translator.hpp b/include/boost/statechart/exception_translator.hpp index 3e9ccc5..27fff04 100644 --- a/include/boost/statechart/exception_translator.hpp +++ b/include/boost/statechart/exception_translator.hpp @@ -23,11 +23,12 @@ namespace fsm ////////////////////////////////////////////////////////////////////////////// -class exception_thrown : public event {}; +class exception_thrown : public event< exception_thrown > {}; ////////////////////////////////////////////////////////////////////////////// +template< class ExceptionEvent = exception_thrown > struct exception_translator { template< class Action, class ExceptionEventHandler > @@ -42,7 +43,7 @@ struct exception_translator } catch ( ... ) { - if ( !eventHandler( exception_thrown() ) ) + if ( !eventHandler( ExceptionEvent() ) ) { throw; } diff --git a/include/boost/statechart/rtti_policy.hpp b/include/boost/statechart/rtti_policy.hpp new file mode 100644 index 0000000..bf947f2 --- /dev/null +++ b/include/boost/statechart/rtti_policy.hpp @@ -0,0 +1,88 @@ +#ifndef BOOST_FSM_RTTI_POLICY_HPP_INCLUDED +#define BOOST_FSM_RTTI_POLICY_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 // std::type_info + + + +#ifdef BOOST_MSVC +// We permanently turn off the following level 4 warnings because users will +// have to do so themselves anyway if we turn them back on +#pragma warning( disable: 4511 ) // copy constructor could not be generated +#pragma warning( disable: 4512 ) // assignment operator could not be generated +#endif + + + +namespace boost +{ +namespace fsm +{ + + + +////////////////////////////////////////////////////////////////////////////// +struct rtti_policy +{ + typedef const std::type_info & id_type; + + //////////////////////////////////////////////////////////////////////////// + template< class Base > + class base_type : public Base + { + public: + //////////////////////////////////////////////////////////////////////// + id_type dynamic_type() const + { + // On MSVC7.1 event dispatch is quite a bit faster when each object + // stores a reference to its type_info object during construction + // instead of returning typeid( *this ) here. + // The resulting increase in state size will not be noticeable under + // almost all circumstances (the memory footprint of a very complex + // state machine could grow by maybe 100 bytes). + return id_; + } + + protected: + //////////////////////////////////////////////////////////////////////// + base_type( id_type id ) : id_( id ) {} + + private: + //////////////////////////////////////////////////////////////////////// + id_type id_; + }; + + //////////////////////////////////////////////////////////////////////////// + template< class MostDerived, class Base > + class derived_type : public Base + { + public: + //////////////////////////////////////////////////////////////////////// + static id_type static_type() + { + return typeid( MostDerived ); + } + + protected: + //////////////////////////////////////////////////////////////////////// + derived_type() : Base( static_type() ) {} + }; +}; + + + +} // namespace fsm +} // namespace boost + + + +#endif diff --git a/include/boost/statechart/simple_state.hpp b/include/boost/statechart/simple_state.hpp index 95d7af2..7a7fd4d 100644 --- a/include/boost/statechart/simple_state.hpp +++ b/include/boost/statechart/simple_state.hpp @@ -42,7 +42,6 @@ #include // boost::polymorphic_downcast #include // BOOST_STATIC_CONSTANT -#include // std::type_info namespace boost @@ -66,10 +65,12 @@ struct make_list : public mpl::apply_if< using namespace mpl::placeholders; ////////////////////////////////////////////////////////////////////////////// -template< class Context, class InnerInitial > -struct state_base_type +template< class Derived, class Context, class InnerInitial > +struct simple_state_base_type { private: + typedef typename Context::state_list_type state_list_type; + typedef typename Context::rtti_policy_type rtti_policy_type; // TODO: Check that position in inner initial list corresponds to // orthogonal_position typedef typename detail::make_list< InnerInitial >::type @@ -78,10 +79,15 @@ struct state_base_type public: typedef typename mpl::apply_if< mpl::empty< inner_initial_list >, - mpl::identity< leaf_state< typename Context::state_list_type > >, - mpl::identity< node_state< - mpl::size< inner_initial_list >::type::value, - typename Context::state_list_type > > >::type type; + mpl::identity< typename rtti_policy_type:: + template derived_type< Derived, leaf_state< + state_list_type, + rtti_policy_type > > >, + mpl::identity< typename rtti_policy_type:: + template derived_type< Derived, node_state< + mpl::size< inner_initial_list >::type::value, + state_list_type, + rtti_policy_type > > > >::type type; }; @@ -237,11 +243,11 @@ template< class Derived, class Context, // either an outer state or a state_machine class Reactions = no_reactions, class InnerInitial = detail::empty_list > // initial inner state -class simple_state : public detail::state_base_type< +class simple_state : public detail::simple_state_base_type< Derived, typename Context::inner_context_type, InnerInitial >::type { - typedef typename detail::state_base_type< - typename Context::inner_context_type, + typedef typename detail::simple_state_base_type< + Derived, typename Context::inner_context_type, InnerInitial >::type base_type; public: @@ -250,7 +256,7 @@ class simple_state : public detail::state_base_type< BOOST_STATIC_CONSTANT( detail::orthogonal_position_type, orthogonal_position = Context::inner_orthogonal_position ); - typedef typename context_type::event_ptr_type event_ptr_type; + typedef typename context_type::event_base_ptr_type event_base_ptr_type; typedef simple_state my_base; @@ -281,7 +287,7 @@ class simple_state : public detail::state_base_type< return context_impl( static_cast< OtherContext * >( 0 ) ); } - void post_event( const event_ptr_type & pEvent ) + void post_event( const event_base_ptr_type & pEvent ) { top_context().post_event( pEvent ); } @@ -303,20 +309,20 @@ class simple_state : public detail::state_base_type< result discard_event() { - state_base::reaction_initiated(); + state_base_type::reaction_initiated(); return do_discard_event; } result forward_event() { - state_base::reaction_initiated(); + state_base_type::reaction_initiated(); return do_forward_event; } result defer_event() { - state_base::reaction_initiated(); - state_base::defer_event(); + state_base_type::reaction_initiated(); + state_base_type::defer_event(); return do_defer_event; } @@ -346,7 +352,7 @@ class simple_state : public detail::state_base_type< // terminated. result terminate() { - state_base::reaction_initiated(); + state_base_type::reaction_initiated(); top_context().terminate( *this ); return do_discard_event; } @@ -361,7 +367,7 @@ class simple_state : public detail::state_base_type< // can be called before the context is set. if ( get_pointer( pContext_ ) != 0 ) { - if ( deferred_events() ) + if ( state_base_type::deferred_events() ) { top_context().release_events( this ); } @@ -372,7 +378,7 @@ class simple_state : public detail::state_base_type< public: ////////////////////////////////////////////////////////////////////////// - // CAUTION: The following declarations should be private. + // The following declarations should be private. // They are only public because many compilers lack template friends. ////////////////////////////////////////////////////////////////////////// // TODO: check that this state really has such an inner orthogonal state @@ -381,6 +387,10 @@ class simple_state : public detail::state_base_type< detail::orthogonal_position_type, inner_orthogonal_position = 0 ); + typedef typename context_type::state_base_type state_base_type; + typedef typename context_type::event_base_type event_base_type; + typedef typename context_type::rtti_policy_type rtti_policy_type; + typedef typename context_type::top_context_type top_context_type; typedef typename context_type::inner_context_ptr_type context_ptr_type; typedef typename context_type::state_list_type state_list_type; @@ -393,9 +403,10 @@ class simple_state : public detail::state_base_type< virtual result react_impl( - const event & evt, const std::type_info & eventType ) + const event_base_type & evt, + typename rtti_policy_type::id_type eventType ) { - enable_reaction(); + state_base_type::enable_reaction(); typedef detail::make_list< Reactions >::type reaction_list; result reactionResult = local_react_impl< reaction_list >( evt, eventType ); @@ -410,7 +421,7 @@ class simple_state : public detail::state_base_type< return reactionResult; } - virtual detail::state_base * outer_state_ptr() const + virtual state_base_type * outer_state_ptr() const { return outer_state_ptr_impl< is_same< top_context_type, Context >::value >(); @@ -588,7 +599,8 @@ class simple_state : public detail::state_base_type< template< class ReactionList > result local_react_impl( - const event & evt, const std::type_info & eventType ) + const event_base_type & evt, + typename rtti_policy_type::id_type eventType ) { result reactionResult = mpl::front< ReactionList >::type::react( *polymorphic_downcast< Derived * >( this ), evt, eventType ); @@ -605,19 +617,19 @@ class simple_state : public detail::state_base_type< template<> result local_react_impl< no_reactions >( - const event &, const std::type_info & ) + const event_base_type &, typename rtti_policy_type::id_type ) { return do_forward_event; } template< bool isOutermost > - detail::state_base * outer_state_ptr_impl() const + state_base_type * outer_state_ptr_impl() const { return get_pointer( pContext_ ); } template<> - detail::state_base * outer_state_ptr_impl< true >() 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 d4833db..5674f6b 100644 --- a/include/boost/statechart/state.hpp +++ b/include/boost/statechart/state.hpp @@ -56,7 +56,7 @@ class state : public simple_state< Derived, Context, Reactions, InnerInitial > public: ////////////////////////////////////////////////////////////////////////// - // CAUTION: The following declarations should be private. + // The following declarations should be private. // They are only public because many compilers lack template friends. ////////////////////////////////////////////////////////////////////////// // See base class for documentation diff --git a/include/boost/statechart/state_machine.hpp b/include/boost/statechart/state_machine.hpp index 97b1429..0672b12 100644 --- a/include/boost/statechart/state_machine.hpp +++ b/include/boost/statechart/state_machine.hpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -39,7 +40,7 @@ #endif #include // std::allocator -#include // std::type_info, std::bad_cast +#include // std::bad_cast @@ -61,23 +62,25 @@ namespace detail -template< class StateList > +template< class StateList, class RttiPolicy > class universal_state; -template< orthogonal_position_type noOfOrthogonalRegions, class StateList > +template< orthogonal_position_type noOfOrthogonalRegions, + class StateList, class RttiPolicy > class node_state; -template< class StateList > +template< class StateList, class RttiPolicy > class leaf_state; ////////////////////////////////////////////////////////////////////////////// +template< class StateBaseType, class EventBaseType, class IdType > class send_function { public: ////////////////////////////////////////////////////////////////////////// send_function( - state_base & toState, - const event & evt, - const std::type_info & eventType + StateBaseType & toState, + const EventBaseType & evt, + IdType eventType ) : toState_( toState ), evt_( evt ), eventType_( eventType ) { @@ -90,74 +93,82 @@ class send_function private: ////////////////////////////////////////////////////////////////////////// - state_base & toState_; - const event & evt_; - const std::type_info & eventType_; + StateBaseType & toState_; + const EventBaseType & evt_; + IdType eventType_; }; ////////////////////////////////////////////////////////////////////////////// -template< bool pointerTarget > +template< class IdType > struct state_cast_impl { - public: - ////////////////////////////////////////////////////////////////////////// - static const state_base * deref_if_necessary( const state_base * pState ) + template< bool pointerTarget > + struct impl + { + public: + ////////////////////////////////////////////////////////////////////////// + template< class StateBaseType > + static const StateBaseType * deref_if_necessary( + const StateBaseType * pState ) + { + return pState; + } + + template< class Target > + static IdType type() + { + Target p = 0; + return type_impl( p ); + } + + static bool found( const void * pFound ) + { + return pFound != 0; + } + + template< class Target > + static Target not_found() + { + return 0; + } + + private: + ////////////////////////////////////////////////////////////////////////// + template< class Type > + static IdType type_impl( const Type * ) + { + return Type::static_type(); + } + }; + + template<> + struct impl< false > + { + template< class StateBaseType > + static const StateBaseType & deref_if_necessary( + const StateBaseType * pState ) { - return pState; + return *pState; } template< class Target > - static const type_info & type() + static IdType type() { - Target p = 0; - return type_impl( p ); + return Target::static_type(); } - static bool found( const void * pFound ) + static bool found( ... ) { - return pFound != 0; + return true; } template< class Target > static Target not_found() { - return 0; + throw std::bad_cast(); } - - private: - ////////////////////////////////////////////////////////////////////////// - template< class Type > - static const type_info & type_impl( const Type * ) - { - return typeid( const Type ); - } -}; - -template<> -struct state_cast_impl< false > -{ - static const state_base & deref_if_necessary( const state_base * pState ) - { - return *pState; - } - - template< class Target > - static const type_info & type() - { - return typeid( Target ); - } - - static bool found( ... ) - { - return true; - } - - template< class Target > - static Target not_found() - { - throw std::bad_cast(); - } + }; }; @@ -170,14 +181,17 @@ struct state_cast_impl< false > // Base class for all state machines // Some function names were derived from a state machine by Aleksey Gurtovoy. template< class Derived, - class InitialState, - class ExceptionTranslator = exception_translator, - class Allocator = std::allocator< void > > + class InitialState, + class Allocator = std::allocator< void >, + class ExceptionTranslator = exception_translator<>, + class RttiPolicy = rtti_policy > class state_machine : private noncopyable { public: ////////////////////////////////////////////////////////////////////////// - typedef intrusive_ptr< const event > event_ptr_type; + typedef RttiPolicy rtti_policy_type; + typedef event_base< rtti_policy_type > event_base_type; + typedef intrusive_ptr< const event_base_type > event_base_ptr_type; void initiate() { @@ -195,7 +209,7 @@ class state_machine : private noncopyable } - void process_event( const event & evt ) + void process_event( const event_base_type & evt ) { send_event( evt ); process_queued_events(); @@ -214,14 +228,15 @@ class state_machine : private noncopyable template< class Target > Target state_cast() const { - typedef detail::state_cast_impl< is_pointer< Target >::value > impl; + typedef detail::state_cast_impl< rtti_policy_type::id_type >:: + impl< is_pointer< Target >::value > impl; for ( state_list_type::const_iterator pCurrentLeafState = currentStates_.begin(); pCurrentLeafState != currentStates_.end(); ++pCurrentLeafState ) { - const detail::state_base * pCurrentState( + const state_base_type * pCurrentState( get_pointer( *pCurrentLeafState ) ); while ( pCurrentState != 0 ) @@ -264,20 +279,22 @@ class state_machine : private noncopyable template< class Target > Target state_downcast() const { - typedef detail::state_cast_impl< is_pointer< Target >::value > impl; - const type_info & targetType = impl::type< Target >(); + typedef detail::state_cast_impl< rtti_policy_type::id_type >:: + impl< is_pointer< Target >::value > impl; + + rtti_policy_type::id_type targetType = impl::type< Target >(); for ( state_list_type::const_iterator pCurrentLeafState = currentStates_.begin(); pCurrentLeafState != currentStates_.end(); ++pCurrentLeafState ) { - const detail::state_base * pCurrentState( + const state_base_type * pCurrentState( get_pointer( *pCurrentLeafState ) ); while ( pCurrentState != 0 ) { - if ( typeid( *pCurrentState ) == targetType ) + if ( pCurrentState->dynamic_type() == targetType ) { return static_cast< Target >( impl::deref_if_necessary( pCurrentState ) ); @@ -303,9 +320,11 @@ class state_machine : private noncopyable public: ////////////////////////////////////////////////////////////////////////// - // CAUTION: The following declarations should be private. + // The following declarations should be private. // They are only public because many compilers lack template friends. ////////////////////////////////////////////////////////////////////////// + typedef detail::state_base< rtti_policy_type > state_base_type; + typedef Derived inner_context_type; BOOST_STATIC_CONSTANT( detail::orthogonal_position_type, @@ -313,17 +332,20 @@ class state_machine : private noncopyable typedef Derived top_context_type; typedef Derived * inner_context_ptr_type; - typedef intrusive_ptr< detail::state_base > state_ptr_type; + typedef intrusive_ptr< state_base_type > state_base_ptr_type; typedef std::list< - state_ptr_type, - typename Allocator::rebind< state_ptr_type >::other > state_list_type; + state_base_ptr_type, + typename Allocator::rebind< state_base_ptr_type >::other + > state_list_type; typedef mpl::clear< mpl::list<> >::type context_type_list; - result react_impl( const event &, const std::type_info & ) + result react_impl( + const event_base_type &, + typename rtti_policy_type::id_type ) { - return do_discard_event; + return do_forward_event; } @@ -363,7 +385,8 @@ class state_machine : private noncopyable eventQueue_.clear(); } - void terminate( detail::universal_state< state_list_type > & theState ) + void terminate( + detail::universal_state< state_list_type, rtti_policy_type > & theState ) { if ( currentStates_.size() == 1 ) { @@ -379,7 +402,7 @@ class state_machine : private noncopyable } } - void post_event( const event_ptr_type & pEvent ) + void post_event( const event_base_ptr_type & pEvent ) { BOOST_ASSERT( get_pointer( pEvent ) != 0 ); eventQueue_.push_back( pEvent ); @@ -407,8 +430,9 @@ class state_machine : private noncopyable } - void add_inner_state( detail::orthogonal_position_type position, - detail::universal_state< state_list_type > * ) + void add_inner_state( + detail::orthogonal_position_type position, + detail::universal_state< state_list_type, rtti_policy_type > * ) { BOOST_ASSERT( position == 0 ); position; @@ -420,7 +444,7 @@ class state_machine : private noncopyable position; } - void release_events( const detail::state_base * pForState ) + void release_events( const state_base_type * pForState ) { const deferred_map_type::iterator pFound = deferredMap_.find( pForState ); @@ -470,7 +494,7 @@ class state_machine : private noncopyable template< class ExceptionEvent > bool handle_exception_event( const ExceptionEvent & exceptionEvent, - detail::state_base * pCurrentState ) + state_base_type * pCurrentState ) { const machine_status_enum status = machine_status(); @@ -487,16 +511,13 @@ class state_machine : private noncopyable // function by orderly transiting to another state or terminating. // As a result of this, the machine must not be unstable when this // function is left. - detail::state_base * const pHandlingState = + state_base_type * const pHandlingState = status == stable ? pCurrentState : &unstable_state(); BOOST_ASSERT( pHandlingState != 0 ); - const result reactionResult = translator_( - detail::send_function( - *pHandlingState, exceptionEvent, typeid( exceptionEvent ) ), - exception_event_handler( *this, pCurrentState ), - do_discard_event ); + const result reactionResult = pHandlingState->react_impl( + exceptionEvent, exceptionEvent.dynamic_type() ); if ( reactionResult == do_defer_event ) { @@ -518,7 +539,7 @@ class state_machine : private noncopyable ////////////////////////////////////////////////////////////////////// exception_event_handler( state_machine & machine, - detail::state_base * pCurrentState = 0 + state_base_type * pCurrentState = 0 ) : machine_( machine ), pCurrentState_( pCurrentState ) @@ -528,13 +549,14 @@ class state_machine : private noncopyable template< class ExceptionEvent > bool operator()( const ExceptionEvent & exceptionEvent ) { - return machine_.handle_exception_event( exceptionEvent, pCurrentState_ ); + return machine_.handle_exception_event( + exceptionEvent, pCurrentState_ ); } private: ////////////////////////////////////////////////////////////////////// state_machine & machine_; - detail::state_base * pCurrentState_; + state_base_type * pCurrentState_; }; friend exception_event_handler; @@ -552,12 +574,12 @@ class state_machine : private noncopyable bool dismissed_; }; - void send_event( const event & evt ) + void send_event( const event_base_type & evt ) { terminator guard( *this ); BOOST_ASSERT( machine_status() != unstable ); - const std::type_info & eventType = typeid( evt ); + rtti_policy_type::id_type eventType = evt.dynamic_type(); result reactionResult = do_forward_event; state_list_type::iterator pState = currentStates_.begin(); @@ -567,8 +589,9 @@ class state_machine : private noncopyable { // CAUTION: The following statement could modify our state list! // We must not continue iterating, if the event was consumed - reactionResult = translator_( - detail::send_function( **pState, evt, eventType ), + reactionResult = translator_( detail::send_function< + state_base_type, event_base_type, rtti_policy_type::id_type >( + **pState, evt, eventType ), exception_event_handler( *this, get_pointer( *pState ) ), do_discard_event ); @@ -587,8 +610,8 @@ class state_machine : private noncopyable void defer_event( - const event & evt, - const detail::state_base * pForState ) + const event_base_type & evt, + const state_base_type * pForState ) { deferredMap_[ pForState ].push_back( evt.clone() ); } @@ -598,7 +621,7 @@ class state_machine : private noncopyable { while ( !eventQueue_.empty() ) { - const event_ptr_type pCurrentEvent( eventQueue_.front() ); + const event_base_ptr_type pCurrentEvent( eventQueue_.front() ); eventQueue_.pop_front(); send_event( *pCurrentEvent ); } @@ -628,32 +651,32 @@ class state_machine : private noncopyable } } - detail::state_base & unstable_state() + state_base_type & unstable_state() { BOOST_ASSERT( machine_status() == unstable ); return **pUnstableState_; } - template< detail::orthogonal_position_type noOfOrthogonalRegions, - class StateList > - void add_impl( - const detail::node_state< noOfOrthogonalRegions, StateList > & ) {} + template< detail::orthogonal_position_type noOfOrthogonalRegions > + void add_impl( const detail::node_state< + noOfOrthogonalRegions, state_list_type, rtti_policy_type > & ) {} - template< class StateList > - void add_impl( detail::leaf_state< StateList > & theState ) + void add_impl( + detail::leaf_state< state_list_type, rtti_policy_type > & theState ) { theState.set_list_position( pUnstableState_ ); pUnstableState_ = currentStates_.end(); } typedef std::list< - event_ptr_type, - typename Allocator::rebind< event_ptr_type >::other > event_queue_type; + event_base_ptr_type, + typename Allocator::rebind< event_base_ptr_type >::other + > event_queue_type; typedef std::map< - const detail::state_base *, event_queue_type, - std::less< const detail::state_base * >, - typename Allocator::rebind< std::pair< const detail::state_base * const, + const state_base_type *, event_queue_type, + std::less< const state_base_type * >, + typename Allocator::rebind< std::pair< const state_base_type * const, event_queue_type > >::other > deferred_map_type; event_queue_type eventQueue_; diff --git a/include/boost/statechart/termination.hpp b/include/boost/statechart/termination.hpp index e5d4463..5cd9dd9 100644 --- a/include/boost/statechart/termination.hpp +++ b/include/boost/statechart/termination.hpp @@ -13,8 +13,6 @@ #include #include -#include // std::type_info - namespace boost @@ -28,11 +26,11 @@ namespace fsm template< class Event > struct termination { - template< class State > + template< class State, class EventBase, class IdType > static result react( - State & stt, const event &, const std::type_info & eventType ) + State & stt, const EventBase &, const IdType & eventType ) { - if ( eventType == typeid( const Event ) ) + if ( eventType == Event::static_type() ) { return stt.terminate(); } diff --git a/include/boost/statechart/transition.hpp b/include/boost/statechart/transition.hpp index 94bf418..7431cbb 100644 --- a/include/boost/statechart/transition.hpp +++ b/include/boost/statechart/transition.hpp @@ -10,13 +10,10 @@ -#include #include #include // boost::polymorphic_downcast -#include // std::type_info - namespace boost @@ -50,11 +47,11 @@ struct transition { private: ////////////////////////////////////////////////////////////////////////// - template< class State > + template< class State, class EventBase > struct impl { template< class TransitionContext2 > - static result react( State & stt, const event & toEvent ) + static result react( State & stt, const EventBase & toEvent ) { return stt.transit< Destination >( pTransitionAction, *polymorphic_downcast< const Event * >( &toEvent ) ); @@ -62,7 +59,7 @@ struct transition template<> static result react< detail::no_context >( - State & stt, const event & ) + State & stt, const EventBase & ) { return stt.transit< Destination >(); } @@ -70,13 +67,14 @@ struct transition public: ////////////////////////////////////////////////////////////////////////// - template< class State > + template< class State, class EventBase, class IdType > static result react( - State & stt, const event & evt, const std::type_info & eventType ) + State & stt, const EventBase & evt, const IdType & eventType ) { - if ( eventType == typeid( const Event ) ) + if ( eventType == Event::static_type() ) { - return impl< State >::react< TransitionContext >( stt, evt ); + return impl< State, EventBase >::react< TransitionContext >( + stt, evt ); } else {