mirror of
https://github.com/ThePhD/sol2.git
synced 2024-03-22 13:10:44 +08:00
430 lines
17 KiB
C++
430 lines
17 KiB
C++
// The MIT License (MIT)
|
|
|
|
// Copyright (c) 2013-2016 Rappt1101010z, 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_CALL_HPP
|
|
#define SOL_CALL_HPP
|
|
|
|
#include "wrapper.hpp"
|
|
#include "property.hpp"
|
|
#include "stack.hpp"
|
|
|
|
namespace sol {
|
|
namespace call_detail {
|
|
|
|
template <bool b, typename F>
|
|
inline decltype(auto) pick(std::integral_constant<bool, b>, F&& f) {
|
|
return std::forward<F>(f);
|
|
}
|
|
|
|
template <typename R, typename W>
|
|
inline auto& pick(std::true_type, property_wrapper<R, W>& f) {
|
|
return f.read;
|
|
}
|
|
|
|
template <typename R, typename W>
|
|
inline auto& pick(std::false_type, property_wrapper<R, W>& f) {
|
|
return f.write;
|
|
}
|
|
|
|
template <typename T, typename List>
|
|
struct void_call;
|
|
|
|
template <typename T, typename... Args>
|
|
struct void_call<T, types<Args...>> {
|
|
static void call(Args...) {}
|
|
};
|
|
|
|
template <typename T, int additional_pop = 0>
|
|
struct constructor_match {
|
|
T* obj;
|
|
|
|
constructor_match(T* obj) : obj(obj) {}
|
|
|
|
template <typename Fx, std::size_t I, typename... R, typename... Args>
|
|
int operator()(types<Fx>, index_value<I>, types<R...> r, types<Args...> a, lua_State* L, int, int start) const {
|
|
detail::default_construct func{};
|
|
return stack::call_into_lua<additional_pop, false>(r, a, L, start, func, obj);
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
inline int destruct(lua_State* L) {
|
|
T* obj = stack::get<non_null<T*>>(L, 1);
|
|
std::allocator<T> alloc{};
|
|
alloc.destroy(obj);
|
|
return 0;
|
|
}
|
|
|
|
namespace overload_detail {
|
|
template <std::size_t... M, typename Match, typename... Args>
|
|
inline int overload_match_arity(sol::types<>, std::index_sequence<>, std::index_sequence<M...>, Match&&, lua_State* L, int, int, Args&&...) {
|
|
return luaL_error(L, "sol: no matching function call takes this number of arguments and the specified types");
|
|
}
|
|
|
|
template <typename Fx, typename... Fxs, std::size_t I, std::size_t... In, std::size_t... M, typename Match, typename... Args>
|
|
inline int overload_match_arity(sol::types<Fx, Fxs...>, std::index_sequence<I, In...>, std::index_sequence<M...>, Match&& matchfx, lua_State* L, int fxarity, int start, Args&&... args) {
|
|
typedef lua_bind_traits<meta::unqualified_t<Fx>> traits;
|
|
typedef meta::tuple_types<typename traits::return_type> return_types;
|
|
typedef typename traits::free_args_list args_list;
|
|
typedef typename args_list::indices args_indices;
|
|
// compile-time eliminate any functions that we know ahead of time are of improper arity
|
|
if (meta::find_in_pack_v<index_value<traits::free_arity>, index_value<M>...>::value) {
|
|
return overload_match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<M...>(), std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...);
|
|
}
|
|
if (traits::free_arity != fxarity) {
|
|
return overload_match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<traits::arity, M...>(), std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...);
|
|
}
|
|
if (!stack::stack_detail::check_types<true>().check(args_list(), args_indices(), L, start, no_panic)) {
|
|
return overload_match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<M...>(), std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...);
|
|
}
|
|
return matchfx(types<Fx>(), index_value<I>(), return_types(), args_list(), L, fxarity, start, std::forward<Args>(args)...);
|
|
}
|
|
} // overload_detail
|
|
|
|
template <typename... Functions, typename Match, typename... Args>
|
|
inline int overload_match_arity(Match&& matchfx, lua_State* L, int fxarity, int start, Args&&... args) {
|
|
return overload_detail::overload_match_arity(types<Functions...>(), std::make_index_sequence<sizeof...(Functions)>(), std::index_sequence<>(), std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...);
|
|
}
|
|
|
|
template <typename... Functions, typename Match, typename... Args>
|
|
inline int overload_match(Match&& matchfx, lua_State* L, int start, Args&&... args) {
|
|
int fxarity = lua_gettop(L) - (start - 1);
|
|
return overload_match_arity<Functions...>(std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...);
|
|
}
|
|
|
|
template <typename T, typename... TypeLists, typename Match, typename... Args>
|
|
inline int construct(Match&& matchfx, lua_State* L, int fxarity, int start, Args&&... args) {
|
|
// use same overload resolution matching as all other parts of the framework
|
|
return overload_match_arity<decltype(void_call<T, TypeLists>::call)...>(std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...);
|
|
}
|
|
|
|
template <typename T, typename... TypeLists>
|
|
inline int construct(lua_State* L) {
|
|
static const auto& meta = usertype_traits<T>::metatable;
|
|
int argcount = lua_gettop(L);
|
|
call_syntax syntax = argcount > 0 ? stack::get_call_syntax(L, meta, 1) : call_syntax::dot;
|
|
argcount -= static_cast<int>(syntax);
|
|
|
|
T** pointerpointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));
|
|
T*& referencepointer = *pointerpointer;
|
|
T* obj = reinterpret_cast<T*>(pointerpointer + 1);
|
|
referencepointer = obj;
|
|
reference userdataref(L, -1);
|
|
userdataref.pop();
|
|
|
|
construct<T, TypeLists...>(constructor_match<T>(obj), L, argcount, 1 + static_cast<int>(syntax));
|
|
|
|
userdataref.push();
|
|
luaL_getmetatable(L, &meta[0]);
|
|
if (stack::get<type>(L) == type::nil) {
|
|
lua_pop(L, 1);
|
|
return luaL_error(L, "sol: unable to get usertype metatable");
|
|
}
|
|
|
|
lua_setmetatable(L, -2);
|
|
return 1;
|
|
}
|
|
|
|
template <typename F, bool is_index, bool is_variable, typename = void>
|
|
struct agnostic_lua_call_wrapper {
|
|
static int var_call(std::true_type, lua_State* L, F f) {
|
|
typedef wrapper<meta::unqualified_t<F>> wrap;
|
|
typedef typename wrap::returns_list returns_list;
|
|
typedef typename wrap::free_args_list args_list;
|
|
typedef typename wrap::caller caller;
|
|
return stack::call_into_lua<is_index ? 1 : 2>(returns_list(), args_list(), L, is_index ? 2 : 3, caller(), f);
|
|
}
|
|
|
|
static int var_call(std::false_type, lua_State* L, F f) {
|
|
typedef wrapper<meta::unqualified_t<F>> wrap;
|
|
typedef typename wrap::free_args_list args_list;
|
|
typedef typename wrap::returns_list returns_list;
|
|
typedef typename wrap::caller caller;
|
|
return stack::call_into_lua(returns_list(), args_list(), L, 1, caller(), f);
|
|
}
|
|
|
|
static int call(lua_State* L, F f) {
|
|
return var_call(std::integral_constant<bool, is_variable>(), L, f);
|
|
}
|
|
};
|
|
|
|
template <typename F, bool is_index, bool is_variable>
|
|
struct agnostic_lua_call_wrapper<F, is_index, is_variable, std::enable_if_t<std::is_member_function_pointer<F>::value>> {
|
|
static int call(lua_State* L, F f) {
|
|
typedef wrapper<meta::unqualified_t<F>> wrap;
|
|
typedef typename wrap::returns_list returns_list;
|
|
typedef typename wrap::args_list args_list;
|
|
typedef typename wrap::caller caller;
|
|
typedef typename wrap::object_type object_type;
|
|
|
|
#ifdef SOL_SAFE_USERTYPE
|
|
object_type* o = stack::get<object_type*>(L, 1);
|
|
if (o == nullptr) {
|
|
return luaL_error(L, "sol: received null for 'self' argument (use ':' for accessing member functions, make sure member variables are preceeded by the actual object with '.' syntax)");
|
|
}
|
|
return stack::call_into_lua<is_variable ? 2 : 1>(returns_list(), args_list(), L, is_variable ? 3 : 2, caller(), f, *o);
|
|
#else
|
|
object_type& o = stack::get<object_type&>(L, 1);
|
|
return stack::call_into_lua<is_variable ? 2 : 1>(returns_list(), args_list(), L, is_variable ? 3 : 2, caller(), f, o);
|
|
#endif // Safety
|
|
}
|
|
};
|
|
|
|
template <bool is_index, bool is_variable, typename C>
|
|
struct agnostic_lua_call_wrapper<lua_r_CFunction, is_index, is_variable, C> {
|
|
static int call(lua_State* L, lua_r_CFunction f) {
|
|
return f(L);
|
|
}
|
|
};
|
|
|
|
template <bool is_index, bool is_variable, typename C>
|
|
struct agnostic_lua_call_wrapper<lua_CFunction, is_index, is_variable, C> {
|
|
static int call(lua_State* L, lua_CFunction f) {
|
|
return f(L);
|
|
}
|
|
};
|
|
|
|
template <bool is_index, bool is_variable, typename C>
|
|
struct agnostic_lua_call_wrapper<no_prop, is_index, is_variable, C> {
|
|
static int call(lua_State* L, no_prop) {
|
|
return luaL_error(L, is_index ? "sol: cannot read from a writeonly property" : "sol: cannot write to a readonly property");
|
|
}
|
|
};
|
|
|
|
template <typename F, bool is_variable>
|
|
struct agnostic_lua_call_wrapper<F, false, is_variable, std::enable_if_t<std::is_member_object_pointer<F>::value>> {
|
|
typedef sol::lua_bind_traits<F> traits_type;
|
|
|
|
static int call_assign(std::true_type, lua_State* L, F f) {
|
|
typedef wrapper<meta::unqualified_t<F>> wrap;
|
|
typedef typename wrap::args_list args_list;
|
|
typedef typename wrap::object_type object_type;
|
|
typedef typename wrap::caller caller;
|
|
#ifdef SOL_SAFE_USERTYPE
|
|
object_type* o = stack::get<object_type*>(L, 1);
|
|
if (o == nullptr) {
|
|
if (is_variable) {
|
|
return luaL_error(L, "sol: received nil for 'self' argument (bad '.' access?)");
|
|
}
|
|
return luaL_error(L, "sol: received nil for 'self' argument (pass 'self' as first argument)");
|
|
}
|
|
return stack::call_into_lua<is_variable ? 2 : 1>(types<void>(), args_list(), L, is_variable ? 3 : 2, caller(), f, *o);
|
|
#else
|
|
object_type& o = stack::get<object_type&>(L, 1);
|
|
return stack::call_into_lua<is_variable ? 2 : 1>(types<void>(), args_list(), L, is_variable ? 3 : 2, caller(), f, o);
|
|
#endif // Safety
|
|
}
|
|
|
|
static int call_assign(std::false_type, lua_State* L, F) {
|
|
return luaL_error(L, "sol: cannot write to this variable: copy assignment/constructor not available");
|
|
}
|
|
|
|
static int call_const(std::false_type, lua_State* L, F f) {
|
|
typedef typename traits_type::return_type R;
|
|
return call_assign(std::is_assignable<std::add_lvalue_reference_t<meta::unqualified_t<R>>, R>(), L, f);
|
|
}
|
|
|
|
static int call_const(std::true_type, lua_State* L, F) {
|
|
return luaL_error(L, "sol: cannot write to a readonly (const) variable");
|
|
}
|
|
|
|
static int call(lua_State* L, F f) {
|
|
return call_const(std::is_const<typename traits_type::return_type>(), L, f);
|
|
}
|
|
};
|
|
|
|
template <typename F, bool is_variable>
|
|
struct agnostic_lua_call_wrapper<F, true, is_variable, std::enable_if_t<std::is_member_object_pointer<F>::value>> {
|
|
typedef sol::lua_bind_traits<F> traits_type;
|
|
|
|
static int call(lua_State* L, F f) {
|
|
typedef wrapper<meta::unqualified_t<F>> wrap;
|
|
typedef typename wrap::object_type object_type;
|
|
typedef typename wrap::returns_list returns_list;
|
|
typedef typename wrap::caller caller;
|
|
#ifdef SOL_SAFE_USERTYPE
|
|
object_type* o = stack::get<object_type*>(L, 1);
|
|
if (o == nullptr) {
|
|
if (is_variable) {
|
|
return luaL_error(L, "sol: 'self' argument is nil (bad '.' access?)");
|
|
}
|
|
return luaL_error(L, "sol: 'self' argument is nil (pass 'self' as first argument)");
|
|
}
|
|
return stack::call_into_lua<is_variable ? 2 : 1>(returns_list(), types<>(), L, is_variable ? 3 : 2, caller(), f, *o);
|
|
#else
|
|
object_type& o = stack::get<object_type&>(L, 1);
|
|
return stack::call_into_lua<is_variable ? 2 : 1>(returns_list(), types<>(), L, is_variable ? 3 : 2, caller(), f, o);
|
|
#endif // Safety
|
|
}
|
|
};
|
|
|
|
template <bool is_index, bool is_variable, typename C>
|
|
struct agnostic_lua_call_wrapper<no_construction, is_index, is_variable, C> {
|
|
static int call(lua_State* L, no_construction&) {
|
|
return luaL_error(L, "sol: cannot call this constructor (tagged as non-constructible)");
|
|
}
|
|
};
|
|
|
|
template <typename... Args, bool is_index, bool is_variable, typename C>
|
|
struct agnostic_lua_call_wrapper<bases<Args...>, is_index, is_variable, C> {
|
|
static int call(lua_State*, bases<Args...>&) {
|
|
// Uh. How did you even call this, lul
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
template <typename T, typename F, bool is_index, bool is_variable, typename = void>
|
|
struct lua_call_wrapper : agnostic_lua_call_wrapper<F, is_index, is_variable> {};
|
|
|
|
template <typename T, typename... Args, bool is_index, bool is_variable, typename C>
|
|
struct lua_call_wrapper<T, sol::constructor_list<Args...>, is_index, is_variable, C> {
|
|
typedef sol::constructor_list<Args...> F;
|
|
|
|
static int call(lua_State* L, F&) {
|
|
static const auto& metakey = usertype_traits<T>::metatable;
|
|
int argcount = lua_gettop(L);
|
|
call_syntax syntax = argcount > 0 ? stack::get_call_syntax(L, metakey, 1) : call_syntax::dot;
|
|
argcount -= static_cast<int>(syntax);
|
|
|
|
T** pointerpointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));
|
|
reference userdataref(L, -1);
|
|
T*& referencepointer = *pointerpointer;
|
|
T* obj = reinterpret_cast<T*>(pointerpointer + 1);
|
|
referencepointer = obj;
|
|
|
|
construct<T, Args...>(constructor_match<T, 1>(obj), L, argcount, 1 + static_cast<int>(syntax));
|
|
|
|
userdataref.push();
|
|
luaL_getmetatable(L, &metakey[0]);
|
|
if (stack::get<type>(L) == type::nil) {
|
|
lua_pop(L, 1);
|
|
return luaL_error(L, "sol: unable to get usertype metatable");
|
|
}
|
|
|
|
lua_setmetatable(L, -2);
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
template <typename T, typename... Cxs, bool is_index, bool is_variable, typename C>
|
|
struct lua_call_wrapper<T, sol::constructor_wrapper<Cxs...>, is_index, is_variable, C> {
|
|
typedef sol::constructor_wrapper<Cxs...> F;
|
|
|
|
struct onmatch {
|
|
template <typename Fx, std::size_t I, typename... R, typename... Args>
|
|
int operator()(types<Fx>, index_value<I>, types<R...> r, types<Args...> a, lua_State* L, int, int start, F& f) {
|
|
T** pointerpointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));
|
|
reference userdataref(L, -1);
|
|
T*& referencepointer = *pointerpointer;
|
|
T* obj = reinterpret_cast<T*>(pointerpointer + 1);
|
|
referencepointer = obj;
|
|
|
|
auto& func = std::get<I>(f.set);
|
|
stack::call_into_lua<1, false>(r, a, L, start, func, detail::implicit_wrapper<T>(obj));
|
|
|
|
userdataref.push();
|
|
luaL_getmetatable(L, &usertype_traits<T>::metatable[0]);
|
|
if (stack::get<type>(L) == type::nil) {
|
|
lua_pop(L, 1);
|
|
std::string err = "sol: unable to get usertype metatable for ";
|
|
err += usertype_traits<T>::name;
|
|
return luaL_error(L, err.c_str());
|
|
}
|
|
lua_setmetatable(L, -2);
|
|
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
static int call(lua_State* L, F& f) {
|
|
call_syntax syntax = stack::get_call_syntax(L, usertype_traits<T>::metatable);
|
|
int syntaxval = static_cast<int>(syntax);
|
|
int argcount = lua_gettop(L) - syntaxval;
|
|
return construct<T, meta::pop_front_type_t<meta::function_args_t<Cxs>>...>(onmatch(), L, argcount, 1 + syntaxval, f);
|
|
}
|
|
|
|
};
|
|
|
|
template <typename T, typename Fx, bool is_index, bool is_variable>
|
|
struct lua_call_wrapper<T, sol::destructor_wrapper<Fx>, is_index, is_variable, std::enable_if_t<std::is_void<Fx>::value>> {
|
|
typedef sol::destructor_wrapper<Fx> F;
|
|
|
|
static int call(lua_State* L, F&) {
|
|
return destruct<T>(L);
|
|
}
|
|
};
|
|
|
|
template <typename T, typename Fx, bool is_index, bool is_variable>
|
|
struct lua_call_wrapper<T, sol::destructor_wrapper<Fx>, is_index, is_variable, std::enable_if_t<!std::is_void<Fx>::value>> {
|
|
typedef sol::destructor_wrapper<Fx> F;
|
|
|
|
static int call(lua_State* L, F& f) {
|
|
T* obj = stack::get<non_null<T*>>(L);
|
|
f.fx(detail::implicit_wrapper<T>(obj));
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
template <typename T, typename... Fs, bool is_index, bool is_variable, typename C>
|
|
struct lua_call_wrapper<T, overload_set<Fs...>, is_index, is_variable, C> {
|
|
typedef overload_set<Fs...> F;
|
|
|
|
struct on_match {
|
|
template <typename Fx, std::size_t I, typename... R, typename... Args>
|
|
int operator()(types<Fx>, index_value<I>, types<R...>, types<Args...>, lua_State* L, int, int, F& fx) {
|
|
auto& f = std::get<I>(fx.set);
|
|
return lua_call_wrapper<T, Fx, is_index, is_variable>{}.call(L, f);
|
|
}
|
|
};
|
|
|
|
static int call(lua_State* L, F& fx) {
|
|
return overload_match_arity<Fs...>(on_match(), L, lua_gettop(L), 1, fx);
|
|
}
|
|
};
|
|
|
|
template <typename T, bool is_index, bool is_variable, typename Fx>
|
|
int call_wrapped(lua_State* L, Fx&& fx) {
|
|
return lua_call_wrapper<T, meta::unqualified_t<Fx>, is_index, is_variable>{}.call(L, std::forward<Fx>(fx));
|
|
}
|
|
|
|
template <typename T, typename = void>
|
|
struct is_var_bind : std::false_type {};
|
|
|
|
template <typename T>
|
|
struct is_var_bind<T, std::enable_if_t<std::is_member_object_pointer<T>::value>> : std::true_type {};
|
|
|
|
template <>
|
|
struct is_var_bind<no_prop> : std::true_type {};
|
|
|
|
template <typename R, typename W>
|
|
struct is_var_bind<property_wrapper<R, W>> : std::true_type {};
|
|
|
|
} // call_detail
|
|
|
|
template <typename T>
|
|
struct is_variable_binding : call_detail::is_var_bind<meta::unqualified_t<T>> {};
|
|
|
|
template <typename T>
|
|
struct is_function_binding : meta::neg<is_variable_binding<T>> {};
|
|
|
|
} // sol
|
|
|
|
#endif // SOL_CALL_HPP
|