Note that there are some explanatory texts on larger screens.

plurals
  1. POType_index use, destruction, and synchronization in the state machine implementation
    primarykey
    data
    text
    <p>In an ongoing project there is a need to have multiple finite state machines implemented. A common layer would be nice so that similar checks and balances can be developed. The state machines' requirements are diverse enough so I wanted to have data types to be determined outside of the FSM template as a user responsibility. Also I wanted to have persistent data totally controlled by an FSM implementation author. Finally, thread-safety is a must for state transition in the template.</p> <p>That resulted in what I think is a lean state transition code. The idea is that the FSM template should be both specialized <em>and</em> wrapped into a usable outer class by an FSM implementer. That wrapper class will be a full specific FSM implementation. The FSM template uses data types and logic defined outside of it, but those states and data should not be visible outside of that final wrapper implementation of an FSM.</p> <p>A few questions remain (I am very green by C++ standards). Here they are:</p> <ol> <li>Is using <code>type_index</code> as a map key in this case an oddity or goes along with <code>type_index</code> design intent? Is it unique enough for this purpose?</li> <li>Is that right that the "magic" of <code>shared_ptr</code> will automatically and properly destroy all data instantiated in the wrapper class?</li> <li>Are there destuction order issue somewhere in this template and implementation?</li> <li>Is there a way to test proper destruction and it's order?</li> <li>Is there a way (or a reasonable need) to test that state transitions actually synchronize in the <code>FSM::operator()</code> method?</li> </ol> <p>Below is the template itself and the main Turnstile class, toughly implementing that from <a href="http://en.wikipedia.org/wiki/Finite-state_machine#Example:_a_turnstile" rel="nofollow">the Wikipedia article</a>. The other six implementation files with specific types and state logic are at <a href="https://github.com/didenko/FSM/tree/c6b80473071cf906bb86dc525ac7d991b9834e01" rel="nofollow">my GitHub FSM repository</a> if needed.</p> <p>The FSM template below takes a vector of logic reacting to messages, with the head of the vector defining the initial state. It wraps each message actor in a state object, attaching state data an plumbing to it. When the FSM template runs a state it can also do sanit checks that the returned "next" state key is valied (future, left out for briefness here).</p> <p>Here is the template:</p> <pre><code>#pragma once #include &lt;memory&gt; #include &lt;string&gt; #include &lt;vector&gt; #include &lt;map&gt; #include &lt;typeindex&gt; #include &lt;mutex&gt; namespace tools { template &lt;typename Message, typename Content&gt; class FSM { public: class React { public: virtual std::type_index operator()( const Message &amp; msg, std::shared_ptr&lt;Content&gt; ) { return std::type_index(typeid(this)); }; }; typedef std::vector&lt;std::shared_ptr&lt;React&gt;&gt; Flux; FSM( Flux &amp; flux, std::shared_ptr&lt;Content&gt; ctnt ) : current( std::type_index( typeid( *( flux.at(0) ) ) ) ) , content( ctnt ) { for ( auto rct: flux ) { states[ std::type_index( typeid( *rct ) ) ] = State( rct, content ); }; }; void operator()( const Message &amp; msg ) { std::lock_guard&lt;std::mutex&gt; lock( fsm_mutex ); current = states.at( current )( msg ); }; private: class State { public: State( std::shared_ptr&lt;React&gt; rct, std::shared_ptr&lt;Content&gt; ctnt ) : content( ctnt ), react( rct ) {}; State() : content( nullptr ), react( nullptr ) {}; std::type_index operator()( const Message &amp; message ) { return (*react)( message, content ); } private: std::shared_ptr&lt;Content&gt; content; std::shared_ptr&lt;React&gt; react; }; std::mutex fsm_mutex; std::shared_ptr&lt;Content&gt; content; std::type_index current; std::map&lt;std::type_index, State&gt; states; }; } </code></pre> <p>The implementation class header:</p> <pre><code>#pragma once #include "TurnstileTypes.h" namespace tools { namespace test { namespace fsm { // Implementation of the Turnstile FSM example from http://en.wikipedia.org/wiki/Finite_state_machine#Example:_a_turnstile // Added RESET event to better test state. class Turnstile { public: Turnstile(void); void operator()( const TurnstileEvent &amp; msg ); unsigned long Pushed(); unsigned long Paid(); std::string Now(); private: TurnstileFSM::Flux flux; std::unique_ptr&lt;TurnstileFSM&gt; fsm; std::shared_ptr&lt;TurnstileData&gt; stats; }; } } } </code></pre> <p>The implementation class body:</p> <pre><code>#include "TurnstileTypes.h" #include "TurnstileLocked.h" #include "TurnstileUnlocked.h" #include "Turnstile.h" namespace tools { namespace test { namespace fsm { Turnstile::Turnstile(void) : stats( std::make_shared&lt;TurnstileData&gt;() ) { flux.push_back( std::make_shared&lt;Locked&gt;() ); flux.push_back( std::make_shared&lt;Unlocked&gt;() ); fsm = std::unique_ptr&lt;TurnstileFSM&gt;( new TurnstileFSM( flux, stats ) ); }; void Turnstile::operator()( const TurnstileEvent &amp; msg ) { (*fsm)( msg ); }; unsigned long Turnstile::Pushed() { return stats-&gt;pushes; }; unsigned long Turnstile::Paid() { return stats-&gt;coins; }; std::string Turnstile::Now() { return stats-&gt;state_name; }; } } } </code></pre> <p>Thank you very much for the answers!</p> <p>[update 131120] It may be helpful to mention, that <code>FSM.h</code> is not meant to be a library with a "public interface". More so it is a boilerplate code to get an FSM author going. The actual user FSM interface is defined by that FSM implementation - in the attached example it is in <code>Turnstile.h</code>. As such, the FSM.h template kept be very short, and intended to be read and understood by an FSM author.</p> <p>I would also expect them (myself for now) to understand the documented pitfalls of the template - like the fact that sending a message to any FSM while it is in a <code>State()()</code>method will dead-lock. Which can be caused by a chain of calls. <a href="https://plus.google.com/+VladDidenko/posts/4Aijg6v1wvg" rel="nofollow">Thanks to Florian Philipp comment on Google+</a> for pointing that out.</p> <p>There is also an intent to have least knowledge in FSM about <code>Message</code> and <code>Content</code> types, so that FSM authors have most flexibility while implementing - that includes getting information out of content by something like accessors. That flexibility comes with responsibility of not changing the content.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload