diff --git a/include/boost/sml.hpp b/include/boost/sml.hpp index bbeb365d..e9e6c706 100644 --- a/include/boost/sml.hpp +++ b/include/boost/sml.hpp @@ -783,8 +783,11 @@ struct unique_covariant_dep> { // #2-#7; it is only enabled for a class T without one (any D* converts to const void*). // A T held by value (#1) does not count: a derived dep wins over it, as #8 did before. template +struct has_exact_ref_dep + : integral_constant, TSet>::value || is_base_of, TSet>::value> {}; +template struct has_exact_dep - : integral_constant, TSet>::value || is_base_of, TSet>::value || + : integral_constant::value || is_base_of, TSet>::value || is_base_of, TSet>::value || is_base_of, TSet>::value || @@ -795,6 +798,11 @@ template struct covariant_dep< T, pool, enable_if_t...>>, true_type>::value>> : unique_covariant_dep::type...>> {}; +template +struct covariant_ref_dep {}; +template +struct covariant_ref_dep, enable_if_t<__is_class(T) && !has_exact_ref_dep...>>::value>> + : unique_covariant_dep::type...>> {}; template > constexpr typename TDep::type try_get(const TPool *); // ───────────────────────────────────────────────────────────────────────────── @@ -893,6 +901,29 @@ constexpr bool would_instantiate_missing_ctor_parameter() { return is_same, decltype(try_get(aux::declval()))>::value; } +template +using is_pointer_slot = integral_constant>, remove_reference_t>::value>; +template +using lookup_t = decltype(try_get(aux::declval())); +template +constexpr auto try_get_ref(const TPool *object, int) -> decltype(try_get>(object)) { + return try_get>(object); +} +template +constexpr missing_ctor_parameter try_get_ref(const TPool *, ...) { return {}; } + +// slot_get: a pointer slot only reads a pointer dep and a reference or value slot never reads one; +// a dep of the other kind counts as missing (#727). +template ::value == is_pointer_slot>::value)> +constexpr lookup_t slot_get(const TPool *object) { return try_get(object); } +template ::value && !is_pointer_slot>::value)> +constexpr missing_ctor_parameter slot_get(const TPool *) { return {}; } +template ::value && is_pointer_slot>::value)> +constexpr auto slot_get(const TPool *object) -> decltype(try_get_ref(object, 0)) { return try_get_ref(object, 0); } + // get / cget: direct O(1) access via static_cast — T must be in pool. template constexpr T &get(TPool &p) { return static_cast &>(p).value; } @@ -917,7 +948,7 @@ struct pool : pool_type... { // The init tag selects pool_type_impl's copy-from-pool constructor path. template constexpr pool(init, const pool &p) - : pool_type(try_get>>>(&p))... {} + : pool_type(slot_get>>>(&p))... {} // Sub-SM pool construction: delegate each slot through the init path above // so that reference slots copy into their own backing stores (avoids #504). template diff --git a/test/ft/dependencies.cpp b/test/ft/dependencies.cpp index 533020c7..91bdf73c 100644 --- a/test/ft/dependencies.cpp +++ b/test/ft/dependencies.cpp @@ -499,6 +499,51 @@ test missing_void_ptr_dep_next_to_ref_dep = [] { expect(sm.is(sml::X)); }; +struct base727 { + int val = 5; +}; +struct derived727 : base727 { + derived727() { val = 727; } +}; +struct other727 {}; + +test pointer_slot_is_null_beside_same_type_ref_dep = [] { + struct c727 { + auto operator()() noexcept { + using namespace sml; + auto check = [](base727 &r, base727 *p, other727 &) { expect(5 == r.val && nullptr == p); }; + // clang-format off + return make_transition_table(*idle + event / check = X); + // clang-format on + } + }; + + base727 base; + other727 other; + sml::sm sm{base, other}; + sm.process_event(e1{}); + expect(sm.is(sml::X)); +}; + +test ref_slot_binds_derived_dep_beside_pointer_dep = [] { + struct c727 { + auto operator()() noexcept { + using namespace sml; + auto check = [](base727 &r, base727 *p, other727 &) { expect(727 == r.val && nullptr != p && 5 == p->val); }; + // clang-format off + return make_transition_table(*idle + event / check = X); + // clang-format on + } + }; + + base727 base; + derived727 derived; + other727 other; + sml::sm sm{&base, derived, other}; + sm.process_event(e1{}); + expect(sm.is(sml::X)); +}; + // Issue #485: passing a pointer dependency as an lvalue caused the SM to store // nullptr instead of the actual pointer. Root cause: forwarding reference // deduction wraps a T* lvalue as T*&; the pool init constructor's try_get