sol2/sol/call.hpp
ThePhD 27174aba9c This mega-commit produces simple_usertype to allow for faster compile times by avoiding the use of __index internally. It sacrifices some speed and some storage optimizations and also does not allow variable syntax, but the produced table is directly modifiable.
Adds a `protect()` function to trigger safety for an item.
This commit also optimizes away all instances of virtual function calls for function calls and storage. Will need to test speed to see how it works out.
Closes #133
Closes #134
Closes #135
Closes #136
2016-07-07 16:52:39 -04:00

450 lines
19 KiB
C++

// The MIT License (MIT)
// Copyright (c) 2013-2016 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_CALL_HPP
#define SOL_CALL_HPP
#include "protect.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::free_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(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_match<T, TypeLists...>(constructor_match<T>(obj), L, argcount, 1 + static_cast<int>(syntax));
userdataref.push();
luaL_getmetatable(L, &meta[0]);
if (type_of(L, -1) == 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, bool checked, 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, checked>(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<0, checked>(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 <bool is_index, bool is_variable, bool checked, typename C>
struct agnostic_lua_call_wrapper<lua_r_CFunction, is_index, is_variable, checked, C> {
static int call(lua_State* L, lua_r_CFunction f) {
return f(L);
}
};
template <bool is_index, bool is_variable, bool checked, typename C>
struct agnostic_lua_call_wrapper<lua_CFunction, is_index, is_variable, checked, C> {
static int call(lua_State* L, lua_CFunction f) {
return f(L);
}
};
template <bool is_index, bool is_variable, bool checked, typename C>
struct agnostic_lua_call_wrapper<no_prop, is_index, is_variable, checked, C> {
static int call(lua_State* L, const no_prop&) {
return luaL_error(L, is_index ? "sol: cannot read from a writeonly property" : "sol: cannot write to a readonly property");
}
};
template <bool is_index, bool is_variable, bool checked, typename C>
struct agnostic_lua_call_wrapper<no_construction, is_index, is_variable, checked, C> {
static int call(lua_State* L, const no_construction&) {
return luaL_error(L, "sol: cannot call this constructor (tagged as non-constructible)");
}
};
template <typename... Args, bool is_index, bool is_variable, bool checked, typename C>
struct agnostic_lua_call_wrapper<bases<Args...>, is_index, is_variable, checked, C> {
static int call(lua_State*, const bases<Args...>&) {
// Uh. How did you even call this, lul
return 0;
}
};
template <typename T, typename F, bool is_index, bool is_variable, bool checked = stack::stack_detail::default_check_arguments, typename = void>
struct lua_call_wrapper : agnostic_lua_call_wrapper<F, is_index, is_variable, checked> {};
template <typename T, typename F, bool is_index, bool is_variable, bool checked>
struct lua_call_wrapper<T, F, is_index, is_variable, checked, 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;
typedef std::conditional_t<std::is_void<T>::value, object_type, T> Ta;
#ifdef SOL_SAFE_USERTYPE
object_type* o = static_cast<object_type*>(stack::get<Ta*>(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, checked>(returns_list(), args_list(), L, is_variable ? 3 : 2, caller(), f, *o);
#else
object_type& o = static_cast<object_type&>(stack::get<Ta&>(L, 1));
return stack::call_into_lua<is_variable ? 2 : 1, checked>(returns_list(), args_list(), L, is_variable ? 3 : 2, caller(), f, o);
#endif // Safety
}
};
template <typename T, typename F, bool is_variable, bool checked>
struct lua_call_wrapper<T, F, false, is_variable, checked, 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 std::conditional_t<std::is_void<T>::value, object_type, T> Ta;
typedef typename wrap::caller caller;
#ifdef SOL_SAFE_USERTYPE
object_type* o = static_cast<object_type*>(stack::get<Ta*>(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 = static_cast<object_type&>(stack::get<Ta&>(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 T, typename F, bool is_variable, bool checked>
struct lua_call_wrapper<T, F, true, is_variable, checked, 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 = static_cast<object_type*>(stack::get<T*>(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, checked>(returns_list(), types<>(), L, is_variable ? 3 : 2, caller(), f, *o);
#else
object_type& o = static_cast<object_type&>(stack::get<T&>(L, 1));
return stack::call_into_lua<is_variable ? 2 : 1, checked>(returns_list(), types<>(), L, is_variable ? 3 : 2, caller(), f, o);
#endif // Safety
}
};
template <typename T, typename... Args, bool is_index, bool is_variable, bool checked, typename C>
struct lua_call_wrapper<T, sol::constructor_list<Args...>, is_index, is_variable, checked, C> {
typedef sol::constructor_list<Args...> F;
static int call(lua_State* L, F&) {
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_match<T, Args...>(constructor_match<T, 1>(obj), L, argcount, 1 + static_cast<int>(syntax));
userdataref.push();
luaL_getmetatable(L, &metakey[0]);
if (type_of(L, -1) == 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, bool checked, typename C>
struct lua_call_wrapper<T, sol::constructor_wrapper<Cxs...>, is_index, is_variable, checked, 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) {
const auto& metakey = usertype_traits<T>::metatable;
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, checked>(r, a, L, start, func, detail::implicit_wrapper<T>(obj));
userdataref.push();
luaL_getmetatable(L, &metakey[0]);
if (type_of(L, -1) == 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_match<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, bool checked>
struct lua_call_wrapper<T, sol::destructor_wrapper<Fx>, is_index, is_variable, checked, std::enable_if_t<std::is_void<Fx>::value>> {
typedef sol::destructor_wrapper<Fx> F;
static int call(lua_State* L, const F&) {
return destruct<T>(L);
}
};
template <typename T, typename Fx, bool is_index, bool is_variable, bool checked>
struct lua_call_wrapper<T, sol::destructor_wrapper<Fx>, is_index, is_variable, checked, std::enable_if_t<!std::is_void<Fx>::value>> {
typedef sol::destructor_wrapper<Fx> F;
static int call(lua_State* L, const F& f) {
T& obj = stack::get<T>(L);
f.fx(detail::implicit_wrapper<T>(obj));
return 0;
}
};
template <typename T, typename... Fs, bool is_index, bool is_variable, bool checked, typename C>
struct lua_call_wrapper<T, overload_set<Fs...>, is_index, is_variable, checked, 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, checked>{}.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, typename V, bool is_index, bool is_variable, bool checked, typename C>
struct lua_call_wrapper<T, protect_t<V>, is_index, is_variable, checked, C> {
typedef protect_t<V> F;
static int call(lua_State* L, F& fx) {
return lua_call_wrapper<T, V, is_index, is_variable, true>{}.call(L, fx.value);
}
};
template <typename T, bool is_index, bool is_variable, typename Fx>
inline 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, bool is_index, bool is_variable, typename F>
inline int call_user(lua_State* L) {
auto& fx = stack::get<user<F>>(L, upvalue_index(1));
return call_wrapped<T, is_index, is_variable>(L, 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