Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions include/boost/sml.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,14 @@ template <class T>
constexpr const T *try_get(const pool_type<const T *> *);
template <class T>
constexpr T *try_get(const pool_type<T *> *);
// Covariant overloads: when the pool holds D& (or const D&) where D derives
// from T, return T& (or const T&) via implicit base-class conversion. (#467)
// remove_const_t<T> in the is_same guard prevents matching when T is const D
// (i.e. the same underlying type, just cv-qualified differently).
template <class T, class D, __BOOST_SML_REQUIRES(!aux::is_same<aux::remove_const_t<T>, D>::value && aux::is_base_of<T, D>::value)>
constexpr T &try_get(const pool_type<D &> *);
template <class T, class D, __BOOST_SML_REQUIRES(!aux::is_same<aux::remove_const_t<T>, D>::value && aux::is_base_of<T, D>::value)>
constexpr const T &try_get(const pool_type<const D &> *);
#if defined(BOOST_SML_CREATE_DEFAULT_CONSTRUCTIBLE_DEPS)
template <class T>
struct pool_type_impl<T &, aux::enable_if_t<aux::is_constructible<T>::value && aux::is_constructible<T, const T &>::value>>
Expand Down Expand Up @@ -736,6 +744,16 @@ template <class T>
constexpr const T *try_get(const pool_type<const T *&> *object) {
return object->value;
}
// Covariant overloads: pool holds D& or const D& where D is a subclass of T.
// Allows passing NiceMock<T> (or any derived, non-copyable type) as a T& dep. (#467)
template <class T, class D, typename aux::enable_if<!aux::is_same<aux::remove_const_t<T>, D>::value && aux::is_base_of<T, D>::value, int>::type>
constexpr T &try_get(const pool_type<D &> *object) {
return object->value;
}
template <class T, class D, typename aux::enable_if<!aux::is_same<aux::remove_const_t<T>, D>::value && aux::is_base_of<T, D>::value, int>::type>
constexpr const T &try_get(const pool_type<const D &> *object) {
return object->value;
}
template <class T, class TPool>
constexpr bool would_instantiate_missing_ctor_parameter() {
return is_same<missing_ctor_parameter<T>, decltype(try_get<T>(aux::declval<TPool>()))>::value;
Expand Down
33 changes: 33 additions & 0 deletions test/ft/dependencies.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,39 @@ test ref_dep_copy_from_pool_not_dangling = [] {
sm.process_event(e2{});
};

// Issue #467: passing a derived (non-copyable) type as a base-class reference
// dependency failed to compile after v1.1.3. The pool init copy-constructor
// called try_get<Base>() which found nothing when the pool held Derived&, fell
// back to missing_ctor_parameter<Base>, tried to default-construct Base, and
// then could not bind the temporary to Base&.
// Fix: add covariant try_get overloads for pool_type<D&> and pool_type<const D&>
// constrained on is_base_of<T,D> — returns T& via implicit base-class conversion.
struct base467 {
base467() = default;
base467(const base467 &) = delete; // non-copyable, like NiceMock<T>
int val = 7;
};

struct derived467 : base467 {};

test non_copyable_derived_dep_as_base_ref = [] {
struct c467 {
auto operator()() noexcept {
using namespace sml;
auto check = [](base467 &d) { expect(7 == d.val); };
// clang-format off
return make_transition_table(*idle + event<e1> / check = X);
// clang-format on
}
};

derived467 dep;
// Action takes base467& but we pass derived467 — must compile and work.
sml::sm<c467> sm{dep};
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
Expand Down
Loading