Skip to content

fix template depth explosion with large FSMs sharing dep types (issue #639)#668

Merged
kris-jusiak merged 2 commits into
boost-ext:masterfrom
PavelGuzenfeld:fix/issue-639-template-depth
May 23, 2026
Merged

fix template depth explosion with large FSMs sharing dep types (issue #639)#668
kris-jusiak merged 2 commits into
boost-ext:masterfrom
PavelGuzenfeld:fix/issue-639-template-depth

Conversation

@PavelGuzenfeld
Copy link
Copy Markdown
Contributor

Problem

get_mapping<event<E>, event<E>, T, R> was calling unique_mappings_t on all k+1 state_mappings even though R's k entries were already unique. This caused O(k) nested recursion inside the O(N) outer unique_mappings_impl recursion, for a total instantiation depth of O(2N) when N transitions share the same event type.

With GCC's default -ftemplate-depth=900, an FSM with ~450 transitions on a single event type would hit the limit and fail to compile.

Root cause

// Before: O(k) inner recursion per step — O(2N) total depth
template <class E, class T, class R>
struct get_mapping<event<E>, event<E>, T, R>
    : aux::type_list<event_mappings<E, aux::apply_t<unique_mappings_t,
          aux::join_t<typename R::types, typename T::types>>>> {};

R::types already contains k unique state_mappings. Re-running unique_mappings_t on all k+1 items recurses k levels deep — unnecessarily, since only the single new entry from T needs to be merged.

Fix

Add a more-specific partial specialization after extend_mapping_t that:

  1. Uses is_base_of (O(1)) to check if the new state_mapping's source state is already in R
  2. If present: merges via extend_mapping_t (O(1) pack expansion)
  3. If absent: appends the new state_mapping directly
// After: O(1) depth per merge step — O(N) total depth
template <class E, class NewSM, class... RSMs>
struct get_mapping_event_same_impl { ... };

template <class E, class NewSM, class... RSMs>
struct get_mapping<event<E>, event<E>, event_mappings<E, aux::inherit<NewSM>>,
                   event_mappings<E, aux::inherit<RSMs...>>>
    : get_mapping_event_same_impl<E, NewSM, RSMs...>::type {};

The original general specialization remains as a fallback for any edge cases.

Result

A 450-transition FSM with all transitions sharing the same event type now compiles at default template depth. The original would fail at ~450 transitions.

Test

test/ft/issue_639_repro.cpp — 50 same-event transitions compiled with -ftemplate-depth=100:

  • Without fix: fails (depth ~100)
  • With fix: compiles and runs correctly (depth ~55)

Fixes #639

…ext#659)

Actions that take a submachine struct by reference (e.g. `[](MySubSM &s, ...)`)
were receiving the copy in deps_ rather than the live state in sub_sms_.
sm_impl<sm_policy<T>> inherits from T and is the authoritative state; deps_
holds only a separate, unused copy.

Fix: add 6-parameter get_arg overloads (int/... priority) that look in sub_sms_
first via sub_sm<sm_impl<sm_policy<T>>>::get when T is a sub-SM type.
The current SM's own type (Tsm::sm_t) is excluded to preserve existing
behaviour for top-level SM member-function callbacks.
call::execute (no-policy and logger variants) now pass subs to get_arg;
the logger path also threads subs through execute_impl.

Test: submachine_state_data_persistence in test/ft/composite.cpp
…oost-ext#639)

get_mapping<event<E>, event<E>, T, R> was calling unique_mappings_t on all
k+1 state_mappings even though R's k entries were already unique, causing
O(k) nested recursion inside O(N) outer recursion → total depth O(2N).

Add a more-specific partial specialization that uses is_base_of (O(1)) to
check membership and extend_mapping_t (O(1) pack expansion) to merge,
reducing total instantiation depth from O(2N) to O(N). A 450-transition
FSM sharing the same event type now compiles at default template depth.

Regression test: issue_639_repro.cpp compiled with -ftemplate-depth=100
(50 same-event transitions; unfixed code hits depth ~100 and fails there).
@kris-jusiak kris-jusiak merged commit 87aaa8f into boost-ext:master May 23, 2026
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Almost reached the limit of template instantiation depth

2 participants