Skip to content
Merged
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
11 changes: 11 additions & 0 deletions include/boost/sml.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,17 @@ template <class T>
constexpr T *try_get(const pool_type<T *> *object) {
return object->value;
}
// When a pointer dep is passed as an lvalue the forwarding constructor wraps
// it in T*& (reference-to-pointer). Strip the reference so the pool init
// constructor can find the stored value. (#485)
template <class T>
constexpr T *try_get(const pool_type<T *&> *object) {
return object->value;
}
template <class T>
constexpr const T *try_get(const pool_type<const T *&> *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
48 changes: 48 additions & 0 deletions test/ft/dependencies.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -396,3 +396,51 @@ test ref_dep_copy_from_pool_not_dangling = [] {
// verifying the dep reference is valid (not dangling).
sm.process_event(e2{});
};

// 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
// lookup had no overload for pool_type<T*&>*, so it fell through to the
// missing_ctor_parameter catch-all which returned nullptr.
// Fix: add try_get overloads for reference-to-pointer types (T*& and const T*&).
struct dep485 {
int val = 42;
};

test pointer_dep_lvalue_not_null = [] {
struct c {
auto operator()() noexcept {
using namespace sml;
// action takes non-const pointer: Dep*
auto action = [](dep485* d) { expect(d != nullptr); expect(42 == d->val); };
// clang-format off
return make_transition_table(*idle + event<e1> / action = X);
// clang-format on
}
};

dep485 dep;
dep485* ptr = &dep; // lvalue pointer — was getting stored as nullptr before the fix
sml::sm<c> sm{ptr};
sm.process_event(e1{});
expect(sm.is(sml::X));
};

test const_pointer_dep_lvalue_not_null = [] {
struct c {
auto operator()() noexcept {
using namespace sml;
// action takes const pointer: const Dep*
auto action = [](const dep485* d) { expect(d != nullptr); expect(42 == d->val); };
// clang-format off
return make_transition_table(*idle + event<e1> / action = X);
// clang-format on
}
};

dep485 dep;
dep485* ptr = &dep; // non-const Dep* lvalue: should be implicitly convertible to const Dep*
sml::sm<c> sm{ptr};
sm.process_event(e1{});
expect(sm.is(sml::X));
};
Loading