Skip to content

Problems with reentering a terminated substate. #440

Closed

Description

@lukashausperger

Hey there!

We encountered the following issue:

Expected Behavior

Having a look at the following code snippet, we expect that the SubSubState is at Leave3 after trying to enter it for the second time.

Actual Behavior

When we try to reenter SubSubState in SubState for the second time (after leaving it), it stucks at the termination state X.

Steps to Reproduce the Problem

#include <boost/sml.hpp>
#include <cassert>
#include <deque>
#include <iostream>
#include <queue>

namespace sml = boost::sml;

namespace {
struct event_1 {};
struct event_2 {};
struct event_3 {};
struct event_4 {};

struct Leave1 {};
struct Leave2 {};
struct Leave3 {};

struct SubSubState {
  auto operator()() const noexcept {
    using namespace sml;
    return make_transition_table(*state<Leave3> + event<event_4> / process(event_3{}) = X);
  }
};

struct SubState {
  auto operator()() const noexcept {
    using namespace sml;
    return make_transition_table(*state<SubSubState> + event<event_3> / process(event_2{}) = X);
  }
};

struct StateMachine {
  auto operator()() const noexcept {
    using namespace sml;
    return make_transition_table(*state<Leave1> + event<event_1> = state<SubState>,
                                 state<SubState> + event<event_2> = state<Leave1>);
  }
};
}  // namespace

int main(int argv, char** argc) {
  sml::sm<StateMachine, sml::defer_queue<std::deque>, sml::process_queue<std::queue>> stm;
  using namespace sml;
  stm.process_event(event_1{});
  assert(stm.is<decltype(state<SubState>)>(state<SubSubState>));
  assert(stm.is<decltype(state<SubSubState>)>(state<Leave3>));
  stm.process_event(event_4{});
  stm.process_event(event_1{});
  assert(stm.is<decltype(state<SubState>)>(state<SubSubState>));
  assert(stm.is<decltype(state<SubSubState>)>(state<Leave3>));
}

This will output:

int main(int, char**): Assertion `stm.is<decltype(state<SubSubState>)>(state<Leave3>)' failed.

So the last assertion fails.

Specifications

Version: master
OS: Ubuntu 18.04
Compiler: gcc-10, g++-10

Acutally we found the following workaround which solves this issue:
When we change struct SubState{...} to

struct SubState {
  auto operator()() const noexcept {
    using namespace sml;
    return make_transition_table(*state<Leave2> + sml::on_entry<_> / process(event_5{}),
                                 state<Leave2> + event<event_5> = state<SubSubState>,
                                 state<SubSubState> + event<event_3> / process(event_2{}) = X);
  }
};

the last assertion does not fail anymore and SubSubState is a Leave_3 which is the desired behavior.

The question is why isn't it working without this workaround.

Thanks for your support.

Activity

  1. SchmadenSchmuki commented on Jan 21, 2024

    @SchmadenSchmuki

    The same here (using the same workaround).
    Took me some additional time to understand it, maybe you already know this but my explanation is:

    • When a sub-state-machine (without history) is (re)entered, the initial state is set, (SubState is set to SubSubState,) but this is not performed recursively. The state of SubState's initial state SubSubState is not set.
    • The problem therefore only occurs when the initial state of a sub-state-machine is also a sub-state-machine. With the workaround this is no longer the case. In the workaround, when SubSubState is entered, it get its initial state set the same way SubState gets it set when it is reentered.
      (In my case SubSubState had three states and remained/was in the state it was left in when SubState was left. When SubState was reentered then SubSubState could also have the middle state.)
    • It works fine the first time SubState is entered because there SubSubState is unchanged and has the expected initial state.

    I think it's a bug or at least undesireable because this way SubState behaves differently between the first and the following times it's entered.

    Until now I only used SML and didn't look into the source code, so I have no idea how difficult it would be to fix this.
    In my opinion fixing it would be an improvement because to me this was unexpected behaviour and took some time to figure out. Also eliminating the workaround would simplify my state machines.

  2. jefftrull commented on Oct 13, 2025

    @jefftrull
    Contributor

    Can you see if #649 helps?

  3. SchmadenSchmuki commented on Nov 2, 2025

    @SchmadenSchmuki

    Works for me, don't have the mentioned problems anymore, awesome

  4. PavelGuzenfeld commented on Oct 13, 2026

    @PavelGuzenfeld
    Contributor

    Fixed by #649, the second entry starts at the initial substate on master 4970fef. Can be closed.

  5. PavelGuzenfeld commented on Oct 17, 2026

    @PavelGuzenfeld
    Contributor

    @kris-jusiak can you close this? Fixed or answered, see the comment above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions