// sol2 // The MIT License (MIT) // Copyright (c) 2013-2021 Rapptz, ThePhD and contributors // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of // the Software, and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef SOL_FUNCTION_TYPES_HPP #define SOL_FUNCTION_TYPES_HPP #include #include #include #include #include #include #include namespace sol { namespace function_detail { template struct class_indicator { using type = T; }; struct call_indicator { }; template int lua_c_wrapper(lua_State* L) { lua_CFunction cf = lua_tocfunction(L, lua_upvalueindex(2)); int nr = cf(L); if constexpr (yielding) { return lua_yield(L, nr); } else { return nr; } } template int lua_c_noexcept_wrapper(lua_State* L) noexcept { detail::lua_CFunction_noexcept cf = reinterpret_cast(lua_tocfunction(L, lua_upvalueindex(2))); int nr = cf(L); if constexpr (yielding) { return lua_yield(L, nr); } else { return nr; } } struct c_function_invocation { }; template void select(lua_State* L, Fx&& fx, Args&&... args); template void select_set_fx(lua_State* L, Args&&... args) { lua_CFunction freefunc = no_trampoline ? function_detail::call, 2, is_yielding> : detail::static_trampoline, 2, is_yielding>>; int upvalues = 0; upvalues += stack::push(L, nullptr); upvalues += stack::push>(L, std::forward(args)...); stack::push(L, c_closure(freefunc, upvalues)); } template void select_convertible(types, lua_State* L, Fx&& fx, Args&&... args) { using dFx = std::decay_t>; using fx_ptr_t = R (*)(A...); constexpr bool is_convertible = std::is_convertible_v; if constexpr (is_convertible) { fx_ptr_t fxptr = detail::unwrap(std::forward(fx)); select(L, std::move(fxptr), std::forward(args)...); } else { using F = function_detail::functor_function; select_set_fx(L, std::forward(fx), std::forward(args)...); } } template void select_convertible(types<>, lua_State* L, Fx&& fx, Args&&... args) { typedef meta::function_signature_t> Sig; select_convertible(types(), L, std::forward(fx), std::forward(args)...); } template void select_member_variable(lua_State* L, Fx&& fx, Args&&... args) { using uFx = meta::unqualified_t; if constexpr (sizeof...(Args) < 1) { using C = typename meta::bind_traits::object_type; lua_CFunction freefunc = &function_detail::upvalue_this_member_variable::template call; int upvalues = 0; upvalues += stack::push(L, nullptr); upvalues += stack::stack_detail::push_as_upvalues(L, fx); stack::push(L, c_closure(freefunc, upvalues)); } else if constexpr (sizeof...(Args) < 2) { using Tu = typename meta::meta_detail::unqualified_non_alias::type; constexpr bool is_reference = meta::is_specialization_of_v || std::is_pointer_v; if constexpr (meta::is_specialization_of_v) { lua_CFunction freefunc = &function_detail::upvalue_this_member_variable::template call; int upvalues = 0; upvalues += stack::push(L, nullptr); upvalues += stack::stack_detail::push_as_upvalues(L, fx); stack::push(L, c_closure(freefunc, upvalues)); } else if constexpr (is_reference) { typedef std::decay_t dFx; dFx memfxptr(std::forward(fx)); auto userptr = detail::ptr(std::forward(args)...); lua_CFunction freefunc = &function_detail::upvalue_member_variable, meta::unqualified_t>::template call; int upvalues = 0; upvalues += stack::push(L, nullptr); upvalues += stack::stack_detail::push_as_upvalues(L, memfxptr); upvalues += stack::push(L, static_cast(userptr)); stack::push(L, c_closure(freefunc, upvalues)); } else { using clean_fx = std::remove_pointer_t>; using F = function_detail::member_variable; select_set_fx(L, std::forward(fx), std::forward(args)...); } } else { using C = typename meta::bind_traits::object_type; using clean_fx = std::remove_pointer_t>; using F = function_detail::member_variable; select_set_fx(L, std::forward(fx), std::forward(args)...); } } template void select_member_function_with(lua_State* L, Fx&& fx, T&& obj, Args&&... args) { using dFx = std::decay_t; using Tu = meta::unqualified_t; if constexpr (meta::is_specialization_of_v) { (void)obj; using C = typename Tu::type; lua_CFunction freefunc = &function_detail::upvalue_this_member_function::template call; int upvalues = 0; upvalues += stack::push(L, nullptr); upvalues += stack::push>(L, std::forward(fx), std::forward(args)...); stack::push(L, c_closure(freefunc, upvalues)); } else { constexpr bool is_reference = meta::is_specialization_of_v || std::is_pointer_v; if constexpr (is_reference) { auto userptr = detail::ptr(std::forward(obj)); lua_CFunction freefunc = &function_detail::upvalue_member_function, dFx>::template call; int upvalues = 0; upvalues += stack::push(L, nullptr); upvalues += stack::push>(L, std::forward(fx), std::forward(args)...); upvalues += stack::push(L, lightuserdata_value(static_cast(userptr))); stack::push(L, c_closure(freefunc, upvalues)); } else { using F = function_detail::member_function; select_set_fx(L, std::forward(fx), std::forward(obj), std::forward(args)...); } } } template void select_member_function(lua_State* L, Fx&& fx, Args&&... args) { using dFx = std::decay_t; if constexpr (sizeof...(Args) < 1) { using C = typename meta::bind_traits>::object_type; lua_CFunction freefunc = &function_detail::upvalue_this_member_function::template call; int upvalues = 0; upvalues += stack::push(L, nullptr); upvalues += stack::push>(L, std::forward(fx)); stack::push(L, c_closure(freefunc, upvalues)); } else { select_member_function_with(L, std::forward(fx), std::forward(args)...); } } template void select(lua_State* L, Fx&& fx, Args&&... args) { using uFx = meta::unqualified_t; if constexpr (is_lua_reference_v) { // TODO: hoist into lambda in this case for yielding??? stack::push(L, std::forward(fx), std::forward(args)...); } else if constexpr (is_lua_c_function_v) { if constexpr (no_trampoline) { if (is_yielding) { int upvalues = 0; upvalues += stack::push(L, nullptr); upvalues += stack::push(L, std::forward(fx)); #if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_) if constexpr (std::is_nothrow_invocable_r_v) { detail::lua_CFunction_noexcept cf = &lua_c_noexcept_wrapper; lua_pushcclosure(L, reinterpret_cast(cf), upvalues); } else #endif { lua_CFunction cf = &function_detail::lua_c_wrapper; lua_pushcclosure(L, cf, upvalues); } } else { lua_pushcclosure(L, std::forward(fx), 0); } } else { int upvalues = 0; upvalues += stack::push(L, nullptr); upvalues += stack::push(L, std::forward(fx)); #if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_) if constexpr (std::is_nothrow_invocable_r_v) { detail::lua_CFunction_noexcept cf = &lua_c_noexcept_wrapper; lua_pushcclosure(L, reinterpret_cast(cf), upvalues); } else { lua_CFunction cf = &function_detail::lua_c_wrapper; lua_pushcclosure(L, cf, upvalues); } #else lua_CFunction cf = &function_detail::lua_c_wrapper; lua_pushcclosure(L, cf, upvalues); #endif } } else if constexpr (std::is_function_v>) { std::decay_t target(std::forward(fx), std::forward(args)...); lua_CFunction freefunc = &function_detail::upvalue_free_function::template call; int upvalues = 0; upvalues += stack::push(L, nullptr); upvalues += stack::stack_detail::push_as_upvalues(L, target); stack::push(L, c_closure(freefunc, upvalues)); } else if constexpr (std::is_member_function_pointer_v) { select_member_function(L, std::forward(fx), std::forward(args)...); } else if constexpr (meta::is_member_object_v) { select_member_variable(L, std::forward(fx), std::forward(args)...); } else { select_convertible(types<>(), L, std::forward(fx), std::forward(args)...); } } } // namespace function_detail namespace stack { template struct unqualified_pusher> { template static int push_yielding(lua_State* L, Arg0&& arg0, Args&&... args) { if constexpr (meta::is_specialization_of_v, std::function>) { if constexpr (is_yielding) { return stack::push>(L, detail::yield_tag, std::forward(arg0), std::forward(args)...); } else { return stack::push(L, std::forward(arg0), std::forward(args)...); } } else { function_detail::select(L, std::forward(arg0), std::forward(args)...); return 1; } } template static int push(lua_State* L, Arg0&& arg0, Args&&... args) { if constexpr (std::is_same_v, detail::yield_tag_t>) { push_yielding(L, std::forward(args)...); } else if constexpr (meta::is_specialization_of_v, yielding_t>) { push_yielding(L, std::forward(arg0).func, std::forward(args)...); } else { push_yielding(L, std::forward(arg0), std::forward(args)...); } return 1; } }; template struct unqualified_pusher> { template static int push(lua_State* L, const yielding_t& f, Args&&... args) { if constexpr (meta::is_specialization_of_v, std::function>) { return stack::push(L, detail::yield_tag, f.func, std::forward(args)...); } else { function_detail::select(L, f.func, std::forward(args)...); return 1; } } template static int push(lua_State* L, yielding_t&& f, Args&&... args) { if constexpr (meta::is_specialization_of_v, std::function>) { return stack::push(L, detail::yield_tag, std::move(f.func), std::forward(args)...); } else { function_detail::select(L, std::move(f.func), std::forward(args)...); return 1; } } }; template struct unqualified_pusher> { template static int push_func(std::index_sequence, lua_State* L, FP&& fp) { return stack::push(L, std::get(std::forward(fp).arguments)...); } static int push(lua_State* L, const function_arguments& fp) { return push_func(std::make_index_sequence(), L, fp); } static int push(lua_State* L, function_arguments&& fp) { return push_func(std::make_index_sequence(), L, std::move(fp)); } }; template struct unqualified_pusher> { using TargetFunctor = function_detail::functor_function, false, true>; static int push(lua_State* L, detail::yield_tag_t, const std::function& fx) { if (fx) { function_detail::select_set_fx(L, fx); return 1; } return stack::push(L, lua_nil); } static int push(lua_State* L, detail::yield_tag_t, std::function&& fx) { if (fx) { function_detail::select_set_fx(L, std::move(fx)); return 1; } return stack::push(L, lua_nil); } static int push(lua_State* L, const std::function& fx) { if (fx) { function_detail::select_set_fx(L, fx); return 1; } return stack::push(L, lua_nil); } static int push(lua_State* L, std::function&& fx) { if (fx) { function_detail::select_set_fx(L, std::move(fx)); return 1; } return stack::push(L, lua_nil); } }; template struct unqualified_pusher>> { template static int push(lua_State* L, Args&&... args) { function_detail::select(L, std::forward(args)...); return 1; } }; template struct unqualified_pusher>, meta::neg>, meta::neg>> #if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_) , meta::neg>, meta::neg>> #endif // noexcept function types >::value>> { template static int push(lua_State* L, F&& f) { function_detail::select(L, std::forward(f)); return 1; } }; template struct unqualified_pusher> { static int push(lua_State* L, overload_set&& set) { using F = function_detail::overloaded_function<0, Functions...>; function_detail::select_set_fx(L, std::move(set.functions)); return 1; } static int push(lua_State* L, const overload_set& set) { using F = function_detail::overloaded_function<0, Functions...>; function_detail::select_set_fx(L, set.functions); return 1; } }; template struct unqualified_pusher> { static int push(lua_State* L, protect_t&& pw) { lua_CFunction cf = call_detail::call_user, 2>; int upvalues = 0; upvalues += stack::push(L, nullptr); upvalues += stack::push>>(L, std::move(pw.value)); return stack::push(L, c_closure(cf, upvalues)); } static int push(lua_State* L, const protect_t& pw) { lua_CFunction cf = call_detail::call_user, 2>; int upvalues = 0; upvalues += stack::push(L, nullptr); upvalues += stack::push>>(L, pw.value); return stack::push(L, c_closure(cf, upvalues)); } }; template struct unqualified_pusher> { static int push(lua_State* L, property_wrapper&& pw) { if constexpr (std::is_void_v) { return stack::push(L, std::move(pw.write())); } else if constexpr (std::is_void_v) { return stack::push(L, std::move(pw.read())); } else { return stack::push(L, overload(std::move(pw.read()), std::move(pw.write()))); } } static int push(lua_State* L, const property_wrapper& pw) { if constexpr (std::is_void_v) { return stack::push(L, pw.write); } else if constexpr (std::is_void_v) { return stack::push(L, pw.read); } else { return stack::push(L, overload(pw.read, pw.write)); } } }; template struct unqualified_pusher> { static int push(lua_State* L, var_wrapper&& vw) { return stack::push(L, std::move(vw.value())); } static int push(lua_State* L, const var_wrapper& vw) { return stack::push(L, vw.value()); } }; template struct unqualified_pusher> { static int push(lua_State* L, const factory_wrapper& fw) { using F = function_detail::overloaded_function<0, Functions...>; function_detail::select_set_fx(L, fw.functions); return 1; } static int push(lua_State* L, factory_wrapper&& fw) { using F = function_detail::overloaded_function<0, Functions...>; function_detail::select_set_fx(L, std::move(fw.functions)); return 1; } static int push(lua_State* L, const factory_wrapper& fw, function_detail::call_indicator) { using F = function_detail::overloaded_function<1, Functions...>; function_detail::select_set_fx(L, fw.functions); return 1; } static int push(lua_State* L, factory_wrapper&& fw, function_detail::call_indicator) { using F = function_detail::overloaded_function<1, Functions...>; function_detail::select_set_fx(L, std::move(fw.functions)); return 1; } }; template <> struct unqualified_pusher { static int push(lua_State* L, no_construction) { lua_CFunction cf = &function_detail::no_construction_error; return stack::push(L, cf); } static int push(lua_State* L, no_construction c, function_detail::call_indicator) { return push(L, c); } }; template struct unqualified_pusher> { static int push(lua_State* L, detail::tagged) { lua_CFunction cf = &function_detail::no_construction_error; return stack::push(L, cf); } static int push(lua_State* L, no_construction, function_detail::call_indicator) { lua_CFunction cf = &function_detail::no_construction_error; return stack::push(L, cf); } }; template struct unqualified_pusher>> { static int push(lua_State* L, detail::tagged>) { lua_CFunction cf = call_detail::construct; return stack::push(L, cf); } static int push(lua_State* L, constructor_list) { lua_CFunction cf = call_detail::construct; return stack::push(L, cf); } }; template struct unqualified_pusher> { typedef constructor_list cl_t; static int push(lua_State* L, cl_t cl) { typedef typename meta::bind_traits::return_type T; return stack::push>(L, cl); } }; template struct unqualified_pusher>> { static int push(lua_State* L, detail::tagged>&& c) { return push(L, std::move(c.value())); } static int push(lua_State* L, const detail::tagged>& c) { return push(L, c.value()); } static int push(lua_State* L, constructor_wrapper&& c) { lua_CFunction cf = call_detail::call_user, 2>; int upvalues = 0; upvalues += stack::push(L, nullptr); upvalues += stack::push>>(L, std::move(c)); return stack::push(L, c_closure(cf, upvalues)); } static int push(lua_State* L, const constructor_wrapper& c) { lua_CFunction cf = call_detail::call_user, 2>; int upvalues = 0; upvalues += stack::push(L, nullptr); upvalues += stack::push>>(L, c); return stack::push(L, c_closure(cf, upvalues)); } }; template struct unqualified_pusher> { static int push(lua_State* L, const constructor_wrapper& c) { typedef typename meta::bind_traits::template arg_at<0> arg0; typedef meta::unqualified_t> T; return stack::push>>(L, c); } static int push(lua_State* L, constructor_wrapper&& c) { typedef typename meta::bind_traits::template arg_at<0> arg0; typedef meta::unqualified_t> T; return stack::push>>(L, std::move(c)); } }; template struct unqualified_pusher>> { static int push(lua_State* L, destructor_wrapper) { lua_CFunction cf = detail::usertype_alloc_destroy; return stack::push(L, cf); } }; template struct unqualified_pusher>> { static int push(lua_State* L, destructor_wrapper&& c) { lua_CFunction cf = call_detail::call_user, 2>; int upvalues = 0; upvalues += stack::push(L, nullptr); upvalues += stack::push>>(L, std::move(c)); return stack::push(L, c_closure(cf, upvalues)); } static int push(lua_State* L, const destructor_wrapper& c) { lua_CFunction cf = call_detail::call_user, 2>; int upvalues = 0; upvalues += stack::push(L, nullptr); upvalues += stack::push>>(L, c); return stack::push(L, c_closure(cf, upvalues)); } }; template struct unqualified_pusher> { static int push(lua_State* L, destructor_wrapper&& c) { lua_CFunction cf = call_detail::call_user, 2>; int upvalues = 0; upvalues += stack::push(L, nullptr); upvalues += stack::push>>(L, std::move(c)); return stack::push(L, c_closure(cf, upvalues)); } static int push(lua_State* L, const destructor_wrapper& c) { lua_CFunction cf = call_detail::call_user, 2>; int upvalues = 0; upvalues += stack::push(L, nullptr); upvalues += stack::push>>(L, c); return stack::push(L, c_closure(cf, upvalues)); } }; template struct unqualified_pusher> { using P = policy_wrapper; static int push(lua_State* L, const P& p) { lua_CFunction cf = call_detail::call_user; int upvalues = 0; upvalues += stack::push(L, nullptr); upvalues += stack::push>(L, p); return stack::push(L, c_closure(cf, upvalues)); } static int push(lua_State* L, P&& p) { lua_CFunction cf = call_detail::call_user; int upvalues = 0; upvalues += stack::push(L, nullptr); upvalues += stack::push>(L, std::move(p)); return stack::push(L, c_closure(cf, upvalues)); } }; template struct unqualified_pusher>> { using P = policy_wrapper; using Tagged = detail::tagged; static int push(lua_State* L, const Tagged& p) { lua_CFunction cf = call_detail::call_user; int upvalues = 0; upvalues += stack::push(L, nullptr); upvalues += stack::push>(L, p.value()); return stack::push(L, c_closure(cf, upvalues)); } static int push(lua_State* L, Tagged&& p) { lua_CFunction cf = call_detail::call_user; int upvalues = 0; upvalues += stack::push(L, nullptr); upvalues += stack::push>(L, std::move(p.value())); return stack::push(L, c_closure(cf, upvalues)); } }; template struct unqualified_pusher> { static int push(lua_State* L, push_invoke_t&& pi) { if constexpr (std::is_invocable_v, lua_State*>) { return stack::push(L, std::move(pi.value())(L)); } else { return stack::push(L, std::move(pi.value())()); } } static int push(lua_State* L, const push_invoke_t& pi) { if constexpr (std::is_invocable_v) { return stack::push(L, pi.value()(L)); } else { return stack::push(L, pi.value()()); } } }; namespace stack_detail { template bool check_function_pointer(lua_State* L, int index, Handler&& handler, record& tracking) noexcept { #if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_) tracking.use(1); bool success = lua_iscfunction(L, index) == 1; if (success) { // there must be at LEAST 2 upvalues; otherwise, we didn't serialize it. const char* upvalue_name = lua_getupvalue(L, index, 2); lua_pop(L, 1); success = upvalue_name != nullptr; } if (!success) { // expected type, actual type handler( L, index, type::function, type_of(L, index), "type must be a Lua C Function gotten from a function pointer serialized by sol2"); } return success; #else (void)L; (void)index; (void)handler; (void)tracking; return false; #endif } template Function* get_function_pointer(lua_State* L, int index, record& tracking) noexcept { #if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE_I_) tracking.use(1); auto udata = stack::stack_detail::get_as_upvalues_using_function(L, index); Function* fx = udata.first; return fx; #else (void)L; (void)index; (void)tracking; static_assert(meta::meta_detail::always_true::value, #if SOL_IS_DEFAULT_OFF(SOL_GET_FUNCTION_POINTER_UNSAFE_I_) "You are attempting to retrieve a function pointer type. " "This is inherently unsafe in sol2. In order to do this, you must turn on the " "SOL_GET_FUNCTION_POINTER_UNSAFE configuration macro, as detailed in the documentation. " "Please be careful!" #else "You are attempting to retrieve a function pointer type. " "You explicitly turned off the ability to do this by defining " "SOL_GET_FUNCTION_POINTER_UNSAFE or similar to be off. " "Please reconsider this!" #endif ); return nullptr; #endif } } // namespace stack_detail } // namespace stack } // namespace sol #endif // SOL_FUNCTION_TYPES_HPP