Fix #493: Allow shared_ptr arguments through std::function callbacks

When ChaiScript wraps a script function in a C++ std::function whose
parameter is a shared_ptr (e.g. const std::shared_ptr<T> &), the
Build_Function_Caller_Helper boxed each argument with std::ref. That
produced a Boxed_Value whose Any held a reference_wrapper<const
shared_ptr<T>> with bare type info shared_ptr<T> instead of T, so
dispatch to a C++ function expecting shared_ptr<T> failed. Detect
shared_ptr parameter types in box() and store them by value instead,
letting Boxed_Value's shared_ptr-aware overloads record the correct
bare type and back the Any with a real shared_ptr.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
leftibot 2026-04-28 20:33:53 -06:00
parent 56506bc111
commit 9db4a88b99
2 changed files with 36 additions and 0 deletions

View File

@ -55,10 +55,23 @@ namespace chaiscript::dispatch::detail {
}
}
template<typename T>
struct is_shared_ptr : std::false_type {};
template<typename T>
struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {};
template<typename P, typename Q>
static Boxed_Value box(Q &&q) {
using bare_p = std::remove_cv_t<std::remove_reference_t<P>>;
if constexpr (std::is_same_v<chaiscript::Boxed_Value, std::decay_t<Q>>) {
return std::forward<Q>(q);
} else if constexpr (is_shared_ptr<bare_p>::value) {
// Pass shared_ptr arguments through by value rather than wrapping a
// reference. Boxed_Value's shared_ptr-aware overloads then record the
// bare type as the pointee, allowing dispatch to a C++ function that
// takes the same shared_ptr<T> on the other side.
return Boxed_Value(bare_p(std::forward<Q>(q)));
} else if constexpr (std::is_reference_v<P>) {
return Boxed_Value(std::ref(std::forward<Q>(q)));
} else {

View File

@ -355,6 +355,29 @@ TEST_CASE("Functor cast") {
CHECK(d == 3 * 6);
}
namespace {
int shared_ptr_callback_observed_value = 0;
void shared_ptr_callback_accept(const std::shared_ptr<int> &ptr) { shared_ptr_callback_observed_value = ptr ? *ptr : 0; }
void shared_ptr_callback_call(const std::function<void(const std::shared_ptr<int> &)> &func) { func(std::make_shared<int>(42)); }
} // namespace
// Regression for https://github.com/ChaiScript/ChaiScript/issues/493 - shared_ptr
// arguments could not be round-tripped from C++ through a std::function-wrapped
// ChaiScript callback back into a C++ function expecting a shared_ptr.
TEST_CASE("shared_ptr passed through std::function callback") {
chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
shared_ptr_callback_observed_value = 0;
chai.add(chaiscript::fun(&shared_ptr_callback_accept), "accept");
chai.add(chaiscript::fun(&shared_ptr_callback_call), "call");
CHECK_NOTHROW(chai.eval("call(accept)"));
CHECK(shared_ptr_callback_observed_value == 42);
}
TEST_CASE("Non-ASCII characters in the middle of string") {
chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
CHECK_THROWS_AS(chai.eval<std::string>("prin\xeft \"Hello World\""), chaiscript::exception::eval_error);