Skip to content

fix: allow final classes as SM deps and SM classes (#483)#680

Open
PavelGuzenfeld wants to merge 3 commits into
boost-ext:masterfrom
PavelGuzenfeld:fix/issue-483-final-dep-types
Open

fix: allow final classes as SM deps and SM classes (#483)#680
PavelGuzenfeld wants to merge 3 commits into
boost-ext:masterfrom
PavelGuzenfeld:fix/issue-483-final-dep-types

Conversation

@PavelGuzenfeld
Copy link
Copy Markdown
Contributor

Problem

Closes #483.

Types declared final cannot be used either as SM dependencies or as the SM class itself.
The compiler rejects the code because SML internally tries to inherit from the type in two places.

Root Causes & Fixes

1. is_empty<T>is_empty_base<T> inherits from T

template <class T, class U>
struct is_empty_base : T { U _; };  // ← fails when T is final

Fix: add a partial specialisation guarded by __is_final(T) that inherits from false_type instead, so is_empty_base<T> is never instantiated for final types.

template <class T, class = void>
struct is_empty : aux::integral_constant<bool, sizeof(is_empty_base<T, none_type>) == sizeof(none_type)> {};
// Specialisation: final classes cannot be used as base; treat them as non-empty. (#483)
template <class T>
struct is_empty<T, aux::enable_if_t<bool(__is_final(T))>> : aux::false_type {};

2. sm_impl<Tsm> : Tsm::smsm_impl inherits from the SM class

struct sm_impl : aux::conditional_t<aux::should_not_subclass_statemachine_class<Tsm>::value,
                                     aux::none_type, typename Tsm::sm> {  // ← fails when sm is final

should_not_subclass_statemachine_class previously only returned true for empty SM classes (EBO) or when the dont_instantiate_statemachine_class policy was set.

Fix: extend the predicate to also return true when __is_final(Tsm::sm) is true. This routes final SM classes through the existing composition path (try_get_without_instantiating) which calls operator() on the SM object without requiring inheritance.

struct should_not_subclass_statemachine_class
    : integral_constant<bool, is_empty<typename Tsm::sm>::value ||
                              should_not_instantiate_statemachine_class<Tsm>::value ||
                              bool(__is_final(typename Tsm::sm))> {};

Notes

__is_final is a compiler builtin available on GCC, Clang, and MSVC — used here consistently with __is_base_of already present in the same header.

Tests

Two regression tests added to test/ft/dependencies.cpp:

  • final_dependency_type_compiles — final dep type used in an action lambda
  • final_sm_class_compiles — SM class itself declared final

PavelGuzenfeld and others added 3 commits May 25, 2026 10:53
Two compilation errors triggered when a type marked final is used
either as a dependency or as the SM class itself.

Root cause 1 — is_empty<T>:
  is_empty_base<T, none_type> inherits from T.  The compiler rejects
  this when T is final ('cannot derive from final base').  Fix: add a
  partial specialisation guarded by __is_final(T) that derives from
  false_type instead, so is_empty_base<T> is never instantiated for
  final types.

Root cause 2 — sm_impl<Tsm> : Tsm::sm:
  sm_impl inherits from the user's SM class unless
  should_not_subclass_statemachine_class<Tsm> is true.  That predicate
  previously only checked is_empty (the EBO optimisation path) and the
  explicit dont_instantiate_statemachine_class policy.  Fix: extend the
  predicate to also return true when __is_final(Tsm::sm) is true,
  routing final SM classes through the existing composition path
  (try_get_without_instantiating).

__is_final is a compiler builtin available on GCC, Clang and MSVC, used
here consistently with __is_base_of already present in the same header.

Tests added to test/ft/dependencies.cpp:
  final_dependency_type_compiles — final dep type used in action
  final_sm_class_compiles        — final SM class itself
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.

Dependencies can't be declared final

1 participant