mirror of
https://github.com/ChaiScript/ChaiScript.git
synced 2026-04-30 19:09:26 +08:00
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>
111 lines
4.2 KiB
C++
111 lines
4.2 KiB
C++
// This file is distributed under the BSD License.
|
|
// See "license.txt" for details.
|
|
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
|
|
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
|
|
// http://www.chaiscript.com
|
|
|
|
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
|
|
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
|
|
|
|
#ifndef CHAISCRIPT_FUNCTION_CALL_DETAIL_HPP_
|
|
#define CHAISCRIPT_FUNCTION_CALL_DETAIL_HPP_
|
|
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <vector>
|
|
|
|
#include "boxed_cast.hpp"
|
|
#include "boxed_number.hpp"
|
|
#include "boxed_value.hpp"
|
|
#include "proxy_functions.hpp"
|
|
#include "type_conversions.hpp"
|
|
|
|
namespace chaiscript::dispatch::detail {
|
|
/// used internally for unwrapping a function call's types
|
|
template<typename Ret, typename... Param>
|
|
struct Build_Function_Caller_Helper {
|
|
Build_Function_Caller_Helper(std::vector<Const_Proxy_Function> t_funcs, const Type_Conversions *t_conversions)
|
|
: m_funcs(std::move(t_funcs))
|
|
, m_conversions(t_conversions) {
|
|
}
|
|
|
|
Ret call(const chaiscript::Function_Params ¶ms, const Type_Conversions_State &t_state) {
|
|
if constexpr (std::is_arithmetic_v<Ret> && !std::is_same_v<std::remove_cv_t<std::remove_reference_t<Ret>>, bool>) {
|
|
return Boxed_Number(dispatch::dispatch(m_funcs, params, t_state)).get_as<Ret>();
|
|
} else if constexpr (std::is_same_v<void, Ret>) {
|
|
dispatch::dispatch(m_funcs, params, t_state);
|
|
} else {
|
|
return boxed_cast<Ret>(dispatch::dispatch(m_funcs, params, t_state), &t_state);
|
|
}
|
|
}
|
|
|
|
template<typename... P>
|
|
Ret operator()(P &&...param) {
|
|
std::array<Boxed_Value, sizeof...(P)> params{box<P>(std::forward<P>(param))...};
|
|
|
|
if (m_conversions) {
|
|
Type_Conversions_State state(*m_conversions, m_conversions->conversion_saves());
|
|
return call(chaiscript::Function_Params{params}, state);
|
|
} else {
|
|
Type_Conversions conv;
|
|
Type_Conversions_State state(conv, conv.conversion_saves());
|
|
return call(chaiscript::Function_Params{params}, state);
|
|
}
|
|
}
|
|
|
|
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 {
|
|
return Boxed_Value(std::forward<Q>(q));
|
|
}
|
|
}
|
|
|
|
std::vector<Const_Proxy_Function> m_funcs;
|
|
const Type_Conversions *m_conversions;
|
|
};
|
|
|
|
/// \todo what happens if t_conversions is deleted out from under us?!
|
|
template<typename Ret, typename... Params>
|
|
std::function<Ret(Params...)>
|
|
build_function_caller_helper(Ret(Params...), const std::vector<Const_Proxy_Function> &funcs, const Type_Conversions_State *t_conversions) {
|
|
/*
|
|
if (funcs.size() == 1)
|
|
{
|
|
std::shared_ptr<const Proxy_Function_Impl<Ret (Params...)>> pfi =
|
|
std::dynamic_pointer_cast<const Proxy_Function_Impl<Ret (Params...)> >
|
|
(funcs[0]);
|
|
|
|
if (pfi)
|
|
{
|
|
return pfi->internal_function();
|
|
}
|
|
// looks like this either wasn't a Proxy_Function_Impl or the types didn't match
|
|
// we cannot make any other guesses or assumptions really, so continuing
|
|
}
|
|
*/
|
|
|
|
return std::function<Ret(Params...)>(Build_Function_Caller_Helper<Ret, Params...>(funcs, t_conversions ? t_conversions->get() : nullptr));
|
|
}
|
|
} // namespace chaiscript::dispatch::detail
|
|
|
|
#endif
|