From ffdbc8cefc453876f5134fe6bdaf9c08948af53d Mon Sep 17 00:00:00 2001 From: Pavel Guzenfeld Date: Tue, 13 Oct 2026 23:49:08 +0300 Subject: [PATCH 1/3] fix: value-initialize a missing reference dep (#718) Brace-init from missing_ctor_parameter aggregate-initialized T's first member, ambiguous when that member has a greedy converting ctor like std::expected. A missing vector/string dep is now empty instead of holding one element. --- include/boost/sml.hpp | 3 +- test/ft/CMakeLists.txt | 5 ++ test/ft/issue_718_expected_member.cpp | 97 +++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 test/ft/issue_718_expected_member.cpp diff --git a/include/boost/sml.hpp b/include/boost/sml.hpp index e4dfde27..c11d7bfb 100644 --- a/include/boost/sml.hpp +++ b/include/boost/sml.hpp @@ -751,8 +751,9 @@ template struct pool_type_impl::value && aux::is_constructible::value>> : pool_type_base { constexpr explicit pool_type_impl(T &val) : value{val} {} // bind ref directly + constexpr explicit pool_type_impl(missing_ctor_parameter) : value_{}, value{value_} {}// default-construct backing store template - constexpr explicit pool_type_impl(TObject val) : value_{val}, value{value_} {}// default-construct backing store + constexpr explicit pool_type_impl(TObject val) : value_{val}, value{value_} {} // #504: cross-pool copy — extract T into the local backing store first, then // bind value to it. The naive ': value(i, object)' was a comma-expression // that bound value directly to the pool parameter (dangling after return). diff --git a/test/ft/CMakeLists.txt b/test/ft/CMakeLists.txt index dc1d8393..02e57f22 100644 --- a/test/ft/CMakeLists.txt +++ b/test/ft/CMakeLists.txt @@ -155,4 +155,9 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/issue_552_sub_sm_dep_identity.cpp) add_test(test_issue_552_sub_sm_dep_identity test_issue_552_sub_sm_dep_identity) endif() +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/issue_718_expected_member.cpp) + add_executable(test_issue_718_expected_member issue_718_expected_member.cpp) + add_test(test_issue_718_expected_member test_issue_718_expected_member) +endif() + add_subdirectory(errors) diff --git a/test/ft/issue_718_expected_member.cpp b/test/ft/issue_718_expected_member.cpp new file mode 100644 index 00000000..0e2af678 --- /dev/null +++ b/test/ft/issue_718_expected_member.cpp @@ -0,0 +1,97 @@ +#define BOOST_SML_CREATE_DEFAULT_CONSTRUCTIBLE_DEPS +// cppcheck-suppress missingIncludeSystem +#include +// cppcheck-suppress missingIncludeSystem +#include +// cppcheck-suppress missingIncludeSystem +#include +#if defined(__has_include) +#if __has_include() +// cppcheck-suppress missingIncludeSystem +#include +#endif +#endif +#if defined(__cpp_lib_expected) +// cppcheck-suppress missingIncludeSystem +#include +#endif + +namespace sml = boost::sml; + +struct e718 {}; + +struct greedy718 { + int value{}; + greedy718() = default; + template + greedy718(U&&) : value{-1} {} +}; + +struct greedy_first_member718 { + greedy718 member; +}; + +test default_dep_with_greedy_first_member_is_value_initialized_and_shared = [] { + static int value_on_entry = -2; + static int value_on_event = -2; + + struct c { + auto operator()() const noexcept { + using namespace sml; + return make_transition_table( + *state + event / [](greedy_first_member718& s) { value_on_event = s.member.value; } = X, + state + on_entry<_> / + [](greedy_first_member718& s) { + value_on_entry = s.member.value; + s.member.value = 7; + }); + } + }; + + sml::sm sm; + expect(value_on_entry == 0); + sm.process_event(e718{}); + expect(value_on_event == 7); + expect(sm.is(sml::X)); +}; + +test default_dep_with_initializer_list_ctor_is_empty = [] { + static std::size_t size_on_event = 1; + + struct c { + auto operator()() const noexcept { + using namespace sml; + return make_transition_table(*state + event / [](std::vector& v) { size_on_event = v.size(); } = + X); + } + }; + + sml::sm sm; + sm.process_event(e718{}); + expect(size_on_event == 0u); + expect(sm.is(sml::X)); +}; + +#if defined(__cpp_lib_expected) +struct expected_first_member718 { + std::expected member; +}; + +test default_dep_with_std_expected_first_member_is_shared = [] { + static int value_on_event = 0; + + struct c { + auto operator()() const noexcept { + using namespace sml; + return make_transition_table( + *state + event / [](expected_first_member718& s) { value_on_event = *s.member; } = X, + state + on_entry<_> / [](expected_first_member718& s) { s.member = 1; }); + } + }; + + sml::sm sm; + sm.process_event(e718{}); + expect(value_on_event == 1); + expect(sm.is(sml::X)); +}; +#endif From 7ec73a0d5081d1ebbce938f6b82d50369c11e214 Mon Sep 17 00:00:00 2001 From: Pavel Guzenfeld Date: Wed, 14 Oct 2026 00:29:50 +0300 Subject: [PATCH 2/3] test: silence cppcheck noExplicitConstructor on the greedy ctor (#718) --- test/ft/issue_718_expected_member.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/ft/issue_718_expected_member.cpp b/test/ft/issue_718_expected_member.cpp index 0e2af678..a287a7b9 100644 --- a/test/ft/issue_718_expected_member.cpp +++ b/test/ft/issue_718_expected_member.cpp @@ -24,6 +24,7 @@ struct greedy718 { int value{}; greedy718() = default; template + // cppcheck-suppress noExplicitConstructor greedy718(U&&) : value{-1} {} }; From bd90af8ab674599c1a307e8b3e66f0349652afa4 Mon Sep 17 00:00:00 2001 From: Pavel Guzenfeld Date: Wed, 14 Oct 2026 19:26:26 +0300 Subject: [PATCH 3/3] style: space before trailing comment (#718) --- include/boost/sml.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/sml.hpp b/include/boost/sml.hpp index c11d7bfb..9de0ec8d 100644 --- a/include/boost/sml.hpp +++ b/include/boost/sml.hpp @@ -751,7 +751,7 @@ template struct pool_type_impl::value && aux::is_constructible::value>> : pool_type_base { constexpr explicit pool_type_impl(T &val) : value{val} {} // bind ref directly - constexpr explicit pool_type_impl(missing_ctor_parameter) : value_{}, value{value_} {}// default-construct backing store + constexpr explicit pool_type_impl(missing_ctor_parameter) : value_{}, value{value_} {} // default-construct backing store template constexpr explicit pool_type_impl(TObject val) : value_{val}, value{value_} {} // #504: cross-pool copy — extract T into the local backing store first, then