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
7 changes: 6 additions & 1 deletion include/boost/sml.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -649,8 +649,13 @@ struct pool_type_impl<T &, aux::enable_if_t<aux::is_constructible<T>::value && a
constexpr explicit pool_type_impl(T &value) : value{value} {}
template <class TObject>
constexpr explicit pool_type_impl(TObject value) : value_{value}, value{value_} {}
// Copy-construct from another pool: extract T from the source pool into the
// local backing store, then bind the reference member to it. The old code
// used ': value(i, object)' which is a comma expression — it evaluated
// (i, object) and bound 'value' (T&) directly to the pool parameter
// 'object', leaving a dangling reference once the constructor returned. (#504)
template <class TObject>
constexpr pool_type_impl(const init &i, const TObject &object) : value(i, object) {}
constexpr pool_type_impl(const init &, const TObject &object) : value_{try_get<T>(&object)}, value{value_} {}
T value_{};
T &value;
};
Expand Down
42 changes: 42 additions & 0 deletions test/ft/dependencies.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -353,3 +353,45 @@ test dependencies_multiple_subs = [] {
}
};
#endif

// Issue #504: pool_type_impl<T&>'s (init, object) constructor used a comma
// expression ': value(i, object)' to "initialize" a reference member.
// (i, object) is the C++ comma operator — it evaluates i, discards the result,
// then evaluates object, whose value becomes the initializer. For a T& member
// this bound the reference to the 'object' function parameter, which is a
// dangling reference after the constructor returns.
// Fix: initialize the backing store value_ via try_get, then bind value to value_.
// This test exercises the pool(const pool<TArgs...>&) copy-constructor path that
// instantiates pool_type_impl<T&>(const init&, const TObject&).
struct dep504 {
int val = 99;
};

test ref_dep_copy_from_pool_not_dangling = [] {
// SM with a reference dep (dep504&) and a sub-SM so that the
// pool(const pool<TArgs...>&) path is exercised during sm construction.
struct sub504 {
auto operator()() noexcept {
using namespace sml;
auto check = [](dep504& d) { expect(99 == d.val); };
// clang-format off
return make_transition_table(*idle + event<e2> / check = X);
// clang-format on
}
};

struct top504 {
auto operator()() noexcept {
using namespace sml;
// clang-format off
return make_transition_table(*idle + event<e1> = sml::state<sub504>);
// clang-format on
}
};

dep504 dep;
sml::sm<top504> sm{dep};
sm.process_event(e1{});
sm.process_event(e2{});
expect(sm.is<sml::state<sub504>>(sml::X));
};
Loading