tkem / fsmlite

Lightweight finite state machine framework for C++11
MIT License
156 stars 25 forks source link

Suggestion: target_state() #23

Closed kuuse closed 4 years ago

kuuse commented 4 years ago

Hi,

Maybe I am missing something fundamental here. Besides current_state(), wouldn't it be useful to have target_state()?

An example: I am creating a SIP packet analyzer. At the moment I just want to save the timestamp of the incoming packet. See the transition_table below. As seen in the table, the same Action, SaveTimestamp(), could be used for all packets if target_state() could be accessed from within SaveTimestamp().

Best Regards, Johan

using transition_table = table<
//       Start         Event           Target          Action                  Guard (optional)
//  ----+-------------+---------------+---------------+-----------------------+-----------------+-
    row< BEGIN,        EvInvite,       INVITE,         &m::SaveTimestamp                         >, // Save INVITE ts

    row< INVITE,       EvInvite,       INVITE,         &m::SaveTimestamp                         >, // Save INVITE ts
    row< INVITE,       EvAuth407,      INVITE,         &m::SaveTimestamp                         >, // Save INVITE ts
    row< INVITE,       EvTrying100,    TRYING,         &m::SaveTimestamp                         >, // Save TRYING ts
    row< INVITE,       EvRinging180,   RINGING,        &m::SaveTimestamp                         >, // Save RINGING ts
    row< INVITE,       EvOk200,        INVITE_OK,      &m::SaveTimestamp                         >, // Save INVITE_OK ts
    row< INVITE,       EvFailureXXX,   INVITE_FAILURE, &m::SaveTimestamp                         >, // Save INVITE_FAILURE ts

    row< TRYING,       EvOk200,        INVITE_OK,      &m::SaveTimestamp                         >, // Save INVITE_OK ts
    row< TRYING,       EvRinging180,   RINGING,        &m::SaveTimestamp                         >, // Save RINGING ts
    row< TRYING,       EvCancel,       CANCEL,         &m::SaveTimestamp                         >, // Save CANCEL ts
    row< TRYING,       EvUnsuccessful, UNSUCCESSFUL,   &m::SaveTimestamp                         >, // Save INVITE_FAILURE ts

    row< RINGING,      EvOk200,        INVITE_OK,      &m::SaveTimestamp                         >, // Save INVITE_OK ts
    row< RINGING,      EvCancel,       CANCEL,         &m::SaveTimestamp                         >, // Save CANCEL ts
    row< RINGING,      EvUnsuccessful, UNSUCCESSFUL,   &m::DoNothing                             >,

    row< INVITE_OK,    EvBye,          BYE,            &m::SaveTimestamp                         >, // Save BYE ts

    row< BYE,          EvOk200,        BYE_OK,         &m::SaveTimestamp                         >, // Save BYE_OK ts

    row< CANCEL,       EvOk200,        CANCEL_OK,      &m::SaveTimestamp                         >, // Save CANCEL_OK ts

    row< BYE_OK,       EvAny,          END,            &m::DoNothing                             >,
    row< CANCEL_OK,    EvAny,          END,            &m::DoNothing                             >,
    row< UNSUCCESSFUL, EvAny,          END,            &m::DoNothing                             >
//  ----+-------------+---------------+---------------+-----------------------+-----------------+-
    >;
kuuse commented 4 years ago

Example target_state() implementation:


--- fsmlite.h   2020-06-05 13:22:31.983294087 +0200
+++ fsmlite-target.h    2020-06-05 16:55:37.060732650 +0200
@@ -195,7 +195,7 @@
          *
          * @param init_state the FSM's initial state
          */
-        fsm(state_type init_state = state_type()) : m_state(init_state) {}
+      fsm(state_type init_state = state_type()) : m_state(init_state), m_target(init_state) {}

         /**
          * Process an event.
@@ -220,9 +220,10 @@
         }

         /**
-         * Return the state machine's current state.
+         * Return the state machine's current and target states.
          */
         state_type current_state() const { return m_state; }
+        state_type target_state() const { return m_target; }

     protected:
         /**
@@ -407,6 +408,7 @@
         template<class Event, class T, class... Types>
         struct handle_event<Event, detail::list<T, Types...>> {
             static State execute(Derived& self, const Event& event, State state) {
+                self.m_target = T::target_value();
                 return state == T::start_value() && T::check_guard(self, event) ?
                     T::process_event(self, event), T::target_value() :
                     handle_event<Event, detail::list<Types...>>::execute(self, event, state);
@@ -422,6 +424,7 @@

     private:
         state_type m_state;
+        state_type m_target;

     private:
 #if !defined(NDEBUG) && (!__GNUC__ || __EXCEPTIONS)
tkem commented 4 years ago

Basically, target_state only makes sense while a transition is executing, so I don't think a member function makes much sense here. If the behavior of the action actually depends on the target state, IMHO it is clearer to provide separate action functions in the first place.