mirror of
https://github.com/ThePhD/sol2.git
synced 2024-03-22 13:10:44 +08:00
overloading searching improvements and general refactoring for cleaner implementation.
This commit is contained in:
parent
0ee92c0142
commit
b2b73db5cb
|
@ -318,12 +318,12 @@ struct pusher<function_sig<Sigs...>> {
|
|||
const static char* metatablename = &metakey[0];
|
||||
base_function* target = luafunc.release();
|
||||
void* userdata = reinterpret_cast<void*>(target);
|
||||
lua_CFunction freefunc = &base_function::call;
|
||||
lua_CFunction freefunc = detail::call;
|
||||
|
||||
int metapushed = luaL_newmetatable(L, metatablename);
|
||||
if(metapushed == 1) {
|
||||
lua_pushstring(L, "__gc");
|
||||
stack::push(L, &base_function::gc);
|
||||
stack::push<lua_CFunction>(L, detail::gc);
|
||||
lua_settable(L, -3);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
|
|
@ -175,68 +175,6 @@ public:
|
|||
} // detail
|
||||
|
||||
struct base_function {
|
||||
static int base_call(lua_State* L, void* inheritancedata) {
|
||||
if(inheritancedata == nullptr) {
|
||||
throw error("call from Lua to C++ function has null data");
|
||||
}
|
||||
|
||||
base_function* pfx = static_cast<base_function*>(inheritancedata);
|
||||
base_function& fx = *pfx;
|
||||
int r = fx(L);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int base_gc(lua_State*, void* udata) {
|
||||
if(udata == nullptr) {
|
||||
throw error("call from lua to C++ gc function with null data");
|
||||
}
|
||||
|
||||
base_function* ptr = static_cast<base_function*>(udata);
|
||||
std::default_delete<base_function> dx{};
|
||||
dx(ptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int call(lua_State* L) {
|
||||
void** pinheritancedata = static_cast<void**>(stack::get<upvalue>(L, 1).value);
|
||||
return base_call(L, *pinheritancedata);
|
||||
}
|
||||
|
||||
static int gc(lua_State* L) {
|
||||
void** pudata = static_cast<void**>(stack::get<userdata>(L, 1).value);
|
||||
return base_gc(L, *pudata);
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
struct usertype {
|
||||
static int call(lua_State* L) {
|
||||
// Zero-based template parameter, but upvalues start at 1
|
||||
return base_call(L, stack::get<upvalue>(L, I + 1));
|
||||
}
|
||||
|
||||
template <std::size_t limit>
|
||||
static void func_gc (std::true_type, lua_State*) {
|
||||
|
||||
}
|
||||
|
||||
template <std::size_t limit>
|
||||
static void func_gc (std::false_type, lua_State* L) {
|
||||
// Shut up clang tautological error without throwing out std::size_t
|
||||
for(std::size_t i = 0; i < limit; ++i) {
|
||||
upvalue up = stack::get<upvalue>(L, static_cast<int>(i + 1));
|
||||
base_function* obj = static_cast<base_function*>(up.value);
|
||||
std::allocator<base_function> alloc{};
|
||||
alloc.destroy(obj);
|
||||
alloc.deallocate(obj, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int gc(lua_State* L) {
|
||||
func_gc<I>(Bool<(I < 1)>(), L);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
virtual int operator()(lua_State*) {
|
||||
throw error("failure to call specialized wrapped C++ function from Lua");
|
||||
}
|
||||
|
@ -244,6 +182,69 @@ struct base_function {
|
|||
virtual ~base_function() {}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
static int base_call(lua_State* L, void* inheritancedata) {
|
||||
if (inheritancedata == nullptr) {
|
||||
throw error("call from Lua to C++ function has null data");
|
||||
}
|
||||
|
||||
base_function* pfx = static_cast<base_function*>(inheritancedata);
|
||||
base_function& fx = *pfx;
|
||||
int r = fx(L);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int base_gc(lua_State*, void* udata) {
|
||||
if (udata == nullptr) {
|
||||
throw error("call from lua to C++ gc function with null data");
|
||||
}
|
||||
|
||||
base_function* ptr = static_cast<base_function*>(udata);
|
||||
std::default_delete<base_function> dx{};
|
||||
dx(ptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <std::size_t limit>
|
||||
static void func_gc(std::true_type, lua_State*) {
|
||||
|
||||
}
|
||||
|
||||
template <std::size_t limit>
|
||||
static void func_gc(std::false_type, lua_State* L) {
|
||||
// Shut up clang tautological error without throwing out std::size_t
|
||||
for (std::size_t i = 0; i < limit; ++i) {
|
||||
upvalue up = stack::get<upvalue>(L, static_cast<int>(i + 1));
|
||||
base_function* obj = static_cast<base_function*>(up.value);
|
||||
std::allocator<base_function> alloc{};
|
||||
alloc.destroy(obj);
|
||||
alloc.deallocate(obj, 1);
|
||||
}
|
||||
}
|
||||
|
||||
inline int call(lua_State* L) {
|
||||
void** pinheritancedata = static_cast<void**>(stack::get<upvalue>(L, 1).value);
|
||||
return base_call(L, *pinheritancedata);
|
||||
}
|
||||
|
||||
inline int gc(lua_State* L) {
|
||||
void** pudata = static_cast<void**>(stack::get<userdata>(L, 1).value);
|
||||
return base_gc(L, *pudata);
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
inline int usertype_call(lua_State* L) {
|
||||
// Zero-based template parameter, but upvalues start at 1
|
||||
return base_call(L, stack::get<upvalue>(L, I + 1));
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
inline int usertype_gc(lua_State* L) {
|
||||
func_gc<I>(Bool<(I < 1)>(), L);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // sol
|
||||
|
||||
#endif // SOL_FUNCTION_TYPES_CORE_HPP
|
||||
|
|
|
@ -27,108 +27,105 @@
|
|||
#include "function_types_usertype.hpp"
|
||||
|
||||
namespace sol {
|
||||
namespace detail {
|
||||
template <std::size_t... M, typename Match>
|
||||
inline int match_arity(types<>, std::index_sequence<>, std::index_sequence<M...>, std::ptrdiff_t, lua_State*, Match&&, int) {
|
||||
throw error("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>
|
||||
inline int match_arity(types<Fx, Fxs...>, std::index_sequence<I, In...>, std::index_sequence<M...>, std::ptrdiff_t fxarity, lua_State* L, Match&& matchfx, int start) {
|
||||
typedef function_traits<Fx> traits;
|
||||
typedef tuple_types<typename function_traits<Fx>::return_type> return_types;
|
||||
typedef typename function_traits<Fx>::args_type args_type;
|
||||
typedef typename args_type::indices args_indices;
|
||||
// compile-time eliminate any functions that we know ahead of time are of improper arity
|
||||
if (find_in_pack_v<Index<traits::arity>, Index<M>...>::value || traits::arity != fxarity) {
|
||||
return match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<M...>(), fxarity, L, std::forward<Match>(matchfx), start);
|
||||
}
|
||||
if (traits::arity != fxarity) {
|
||||
return match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<traits::arity, M...>(), fxarity, L, std::forward<Match>(matchfx), start);
|
||||
}
|
||||
if (!detail::check_types(args_type(), args_indices(), L, start)) {
|
||||
return match_arity(types<Fxs...>(), std::index_sequence<In...>(), std::index_sequence<M...>(), fxarity, L, std::forward<Match>(matchfx), start);
|
||||
}
|
||||
return matchfx(Index<I>(), fxarity, L, start);
|
||||
}
|
||||
} // detail
|
||||
|
||||
template <typename... Functions, typename Match>
|
||||
inline int match(std::ptrdiff_t fxarity, lua_State* L, Match&& matchfx, int start = 1) {
|
||||
return detail::match_arity(types<Functions...>(), std::index_sequence_for<Functions...>(), std::index_sequence<>(), fxarity, L, std::forward<Match>(matchfx), start);
|
||||
}
|
||||
|
||||
template <typename... Functions, typename Match>
|
||||
inline int match(lua_State* L, Match&& matchfx, int start = 1) {
|
||||
std::ptrdiff_t fxarity = lua_gettop(L) - (start - 1);
|
||||
return match<Functions...>(fxarity, L, std::forward<Match>(matchfx), start);
|
||||
}
|
||||
|
||||
template <typename... Functions>
|
||||
struct overloaded_function : base_function {
|
||||
typedef std::tuple<std::pair<int, Functions>...> overloads_t;
|
||||
overloads_t overloads;
|
||||
typedef std::tuple<Functions...> overload_list;
|
||||
overload_list overloads;
|
||||
|
||||
overloaded_function(overload_set<Functions...> set)
|
||||
: overloaded_function(indices(), set) {}
|
||||
|
||||
template <std::size_t... I>
|
||||
overloaded_function(std::index_sequence<I...>, overload_set<Functions...> set)
|
||||
: overloaded_function(std::get<In>(set)...) {}
|
||||
|
||||
overloaded_function(Functions... fxs)
|
||||
: overloads({ static_cast<int>(function_traits<Unqualified<Functions>>::arity), fxs }...) {
|
||||
: overloads(fxs...) {
|
||||
|
||||
}
|
||||
|
||||
int match_arity(std::index_sequence<>, lua_State*, std::ptrdiff_t) {
|
||||
throw error("no matching function call takes this number of arguments");
|
||||
}
|
||||
|
||||
template <std::size_t I, std::size_t... In>
|
||||
int match_arity(std::index_sequence<I, In...>, lua_State* L, std::ptrdiff_t x) {
|
||||
// TODO:
|
||||
// when we get proper constexpr, search functions only within the specific
|
||||
// arity range, instead of all of them by using
|
||||
// std::tuple<
|
||||
// std::pair<1-arity, std::tuple<func_arity_of_1_a, func_arity_of_1_b>>,
|
||||
// std::pair<3-arity, std::tuple<func_arity_of_3>>,
|
||||
// std::pair<n-arity, std::tuple<func_arity_of_n, ...>>,
|
||||
// ...
|
||||
//>
|
||||
auto& package = std::get<I>(overloads);
|
||||
auto arity = package.first;
|
||||
if (arity != x) {
|
||||
return match_arity(std::index_sequence<In...>(), L, x);
|
||||
}
|
||||
auto& func = package.second;
|
||||
typedef Unqualified<decltype(func)> fx_t;
|
||||
typedef tuple_types<typename function_traits<fx_t>::return_type> return_types;
|
||||
typedef typename function_traits<fx_t>::args_type args_type;
|
||||
typedef typename args_type::indices args_indices;
|
||||
if (!detail::check_types(args_type(), args_indices(), L)) {
|
||||
return match_arity(std::index_sequence<In...>(), L, x);
|
||||
}
|
||||
return stack::typed_call<false>(return_types(), args_type(), func, L);
|
||||
}
|
||||
|
||||
int match_arity(lua_State* L) {
|
||||
std::ptrdiff_t x = lua_gettop(L);
|
||||
return match_arity(std::make_index_sequence<std::tuple_size<overloads_t>::value>(), L, x);
|
||||
template <std::size_t I>
|
||||
int call(Index<I>, int, lua_State* L, int) {
|
||||
auto& func = std::get<I>(overloads);
|
||||
typedef Unqualified<decltype(func)> Fx;
|
||||
typedef function_traits<Fx> traits;
|
||||
typedef tuple_types<typename function_traits<Fx>::return_type> return_types;
|
||||
typedef typename function_traits<Fx>::args_type args_type;
|
||||
return stack::typed_call<false>(return_types(), args_type(), func, L);
|
||||
}
|
||||
|
||||
virtual int operator()(lua_State* L) override {
|
||||
return match_arity(L);
|
||||
auto mfx = [&](auto&&... args){ return call(std::forward<decltype(args)>(args)...); };
|
||||
return match<Functions...>(L, mfx);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename... Functions>
|
||||
struct usertype_overloaded_function : base_function {
|
||||
typedef std::tuple<std::pair<int, detail::functor<T, Functions>>...> overloads_t;
|
||||
overloads_t overloads;
|
||||
typedef std::tuple<detail::functor<T, Functions>...> overload_list;
|
||||
typedef std::index_sequence_for<Functions...> indices;
|
||||
overload_list overloads;
|
||||
|
||||
usertype_overloaded_function(overload_set<Functions...> set)
|
||||
: usertype_overloaded_function(std::make_index_sequence<sizeof...(Functions)>(), set) {}
|
||||
usertype_overloaded_function(overload_set<Functions...> set) : usertype_overloaded_function(indices(), set) {}
|
||||
|
||||
template<std::size_t... In>
|
||||
usertype_overloaded_function(std::index_sequence<In...>, overload_set<Functions...> set)
|
||||
: usertype_overloaded_function(std::get<In>(set)...) {}
|
||||
template <std::size_t... I>
|
||||
usertype_overloaded_function(std::index_sequence<I...>, overload_set<Functions...> set) : usertype_overloaded_function(std::get<I>(set)...) {}
|
||||
|
||||
usertype_overloaded_function(Functions... fxs) : overloads(fxs...) {}
|
||||
|
||||
usertype_overloaded_function(Functions... fxs)
|
||||
: overloads({static_cast<int>(function_traits<Functions>::arity), fxs}...) {
|
||||
|
||||
}
|
||||
|
||||
int match_arity(std::index_sequence<>, lua_State*, std::ptrdiff_t) {
|
||||
throw error("no matching function call takes this number of arguments");
|
||||
}
|
||||
|
||||
template <std::size_t I, std::size_t... In>
|
||||
int match_arity(std::index_sequence<I, In...>, lua_State* L, std::ptrdiff_t x) {
|
||||
// TODO:
|
||||
// propogate changes from above down here too when they get figured out
|
||||
auto& package = std::get<I>(overloads);
|
||||
auto arity = package.first;
|
||||
if (arity != x) {
|
||||
return match_arity(std::index_sequence<In...>(), L, x);
|
||||
}
|
||||
auto& func = package.second;
|
||||
typedef Unqualified<decltype(func)> fx_t;
|
||||
typedef tuple_types<typename fx_t::return_type> return_types;
|
||||
typedef typename fx_t::args_type args_type;
|
||||
typedef typename args_type::indices args_indices;
|
||||
if (!detail::check_types(args_type(), args_indices(), L, 2)) {
|
||||
return match_arity(std::index_sequence<In...>(), L, x);
|
||||
}
|
||||
template <std::size_t I>
|
||||
int call(Index<I>, int, lua_State* L, int) {
|
||||
auto& func = std::get<I>(overloads);
|
||||
typedef Unqualified<decltype(func)> Fx;
|
||||
typedef typename Fx::traits_type traits;
|
||||
typedef tuple_types<typename Fx::return_type> return_types;
|
||||
typedef typename Fx::args_type args_type;
|
||||
func.item = ptr(stack::get<T>(L, 1));
|
||||
return stack::typed_call<false>(return_types(), args_type(), func, L);
|
||||
}
|
||||
|
||||
int match_arity(lua_State* L) {
|
||||
std::ptrdiff_t x = lua_gettop(L) - 1;
|
||||
return match_arity(std::make_index_sequence<std::tuple_size<overloads_t>::value>(), L, x);
|
||||
virtual int operator()(lua_State* L) override {
|
||||
auto mfx = [&](auto&&... args){ return call(std::forward<decltype(args)>(args)...); };
|
||||
return match<Functions...>(L, mfx, 2);
|
||||
}
|
||||
|
||||
virtual int operator()(lua_State* L) override {
|
||||
return match_arity(L);
|
||||
}
|
||||
};
|
||||
} // sol
|
||||
|
||||
|
|
|
@ -117,11 +117,11 @@ struct usertype_variable_function : public usertype_function_core<Function, Tp>
|
|||
usertype_variable_function(Args&&... args): base_t(std::forward<Args>(args)...) {}
|
||||
|
||||
int prelude(lua_State* L) {
|
||||
this->fx.item = ptr(stack::get<T>(L, 1));
|
||||
int argcount = lua_gettop(L);
|
||||
this->fx.item = stack::get<T*>(L, 1);
|
||||
if(this->fx.item == nullptr) {
|
||||
throw error("userdata for member variable is null");
|
||||
}
|
||||
int argcount = lua_gettop(L);
|
||||
switch(argcount) {
|
||||
case 2:
|
||||
return static_cast<base_t&>(*this)(tuple_types<return_type>(), types<>(), L);
|
||||
|
@ -140,17 +140,23 @@ struct usertype_variable_function : public usertype_function_core<Function, Tp>
|
|||
struct usertype_indexing_function : base_function {
|
||||
std::string name;
|
||||
base_function* original;
|
||||
std::map<std::string, base_function*> functions;
|
||||
std::map<std::string, std::pair<bool, base_function*>> functions;
|
||||
|
||||
template<typename... Args>
|
||||
usertype_indexing_function(std::string name, base_function* original, Args&&... args): name(std::move(name)), original(original), functions(std::forward<Args>(args)...) {}
|
||||
|
||||
int prelude(lua_State* L) {
|
||||
const char* accessor = stack::get<const char*>(L, 1 - lua_gettop(L));
|
||||
auto function = functions.find(accessor);
|
||||
if (function != functions.end()) {
|
||||
return (*function->second)(L);
|
||||
}
|
||||
auto functionpair = functions.find(accessor);
|
||||
if (functionpair != functions.end()) {
|
||||
std::pair<bool, base_function*>& target = functionpair->second;
|
||||
if (target.first) {
|
||||
stack::push<upvalue>(L, target.second);
|
||||
stack::push(L, c_closure(detail::usertype_call<0>, 1));
|
||||
return 1;
|
||||
}
|
||||
return (*target.second)(L);
|
||||
}
|
||||
base_function& core = *original;
|
||||
return core(L);
|
||||
}
|
||||
|
|
|
@ -257,7 +257,8 @@ struct checker<T, type::userdata, C> {
|
|||
handler(L, index, type::userdata, indextype);
|
||||
return false;
|
||||
}
|
||||
const type expectedmetatabletype = static_cast<type>(luaL_getmetatable(L, &usertype_traits<T>::metatable[0]));
|
||||
luaL_getmetatable(L, &usertype_traits<T>::metatable[0]);
|
||||
const type expectedmetatabletype = get<type>(L);
|
||||
if (expectedmetatabletype == type::nil) {
|
||||
lua_pop(L, 2);
|
||||
handler(L, index, type::userdata, indextype);
|
||||
|
@ -316,7 +317,9 @@ struct getter<T*> {
|
|||
type t = type_of(L, index);
|
||||
if (t == type::nil)
|
||||
return nullptr;
|
||||
return std::addressof(getter<T&>{}.get(L, index));
|
||||
void* udata = lua_touserdata(L, index);
|
||||
T** obj = static_cast<T**>(udata);
|
||||
return *obj;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -470,7 +473,7 @@ struct pusher<T, std::enable_if_t<std::is_floating_point<T>::value>> {
|
|||
template<typename T>
|
||||
struct pusher<T, std::enable_if_t<And<std::is_integral<T>, std::is_signed<T>>::value>> {
|
||||
static int push(lua_State* L, const T& value) {
|
||||
lua_pushinteger(L, value);
|
||||
lua_pushinteger(L, static_cast<lua_Integer>(value));
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
@ -859,12 +862,13 @@ inline int typed_call(types<Ret...>, types<Args...> ta, Fx&& fx, lua_State* L, i
|
|||
}
|
||||
|
||||
inline call_syntax get_call_syntax(lua_State* L, const std::string& meta) {
|
||||
if (get<type>(L, 1) == type::table) {
|
||||
if (luaL_newmetatable(L, meta.c_str()) == 0) {
|
||||
lua_settop(L, -2);
|
||||
return call_syntax::colon;
|
||||
}
|
||||
type metatype = stack::get<type>(L);
|
||||
luaL_getmetatable(L, meta.c_str());
|
||||
if (lua_compare(L, -1, -2, LUA_OPEQ) == 1) {
|
||||
lua_pop(L, 1);
|
||||
return call_syntax::colon;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
return call_syntax::dot;
|
||||
}
|
||||
} // stack
|
||||
|
|
|
@ -83,6 +83,9 @@ using Type = typename T::type;
|
|||
template<bool B>
|
||||
using Bool = std::integral_constant<bool, B>;
|
||||
|
||||
template<std::size_t I>
|
||||
using Index = std::integral_constant<std::size_t, I>;
|
||||
|
||||
template<typename T>
|
||||
using Not = Bool<!T::value>;
|
||||
|
||||
|
@ -116,6 +119,12 @@ using Unqualified = std::remove_cv_t<std::remove_reference_t<T>>;
|
|||
template<typename T>
|
||||
using Unwrapped = typename unwrapped<T>::type;
|
||||
|
||||
template<typename V, typename... Vs>
|
||||
struct find_in_pack_v : Bool<false> { };
|
||||
|
||||
template<typename V, typename Vs1, typename... Vs>
|
||||
struct find_in_pack_v<V, Vs1, Vs...> : Or<Bool<(V::value == Vs1::value)>, find_in_pack_v<V, Vs...>> { };
|
||||
|
||||
template<typename... Args>
|
||||
struct return_type {
|
||||
typedef std::tuple<Args...> type;
|
||||
|
@ -253,7 +262,7 @@ struct fx_traits<R(*)(Args...), false> {
|
|||
} // detail
|
||||
|
||||
template<typename Signature>
|
||||
struct function_traits : detail::fx_traits<std::remove_volatile_t<Signature>> {};
|
||||
struct function_traits : detail::fx_traits<std::decay_t<Signature>> {};
|
||||
|
||||
template<typename Signature>
|
||||
using function_args_t = typename function_traits<Signature>::args_type;
|
||||
|
|
113
sol/usertype.hpp
113
sol/usertype.hpp
|
@ -88,11 +88,11 @@ namespace sol {
|
|||
};
|
||||
|
||||
namespace usertype_detail {
|
||||
template<typename T, typename Funcs, typename FuncTable, typename MetaFuncTable, typename VarFuncTable>
|
||||
inline void push_metatable(lua_State* L, Funcs&& funcs, FuncTable&& functable, MetaFuncTable&& metafunctable, VarFuncTable&& varfunctable) {
|
||||
template<typename T, typename Funcs, typename FuncTable, typename MetaFuncTable>
|
||||
inline void push_metatable(lua_State* L, Funcs&& funcs, FuncTable&&, MetaFuncTable&& metafunctable) {
|
||||
luaL_newmetatable(L, &usertype_traits<T>::metatable[0]);
|
||||
int metatableindex = lua_gettop(L);
|
||||
if (funcs.size() < 1 || metafunctable.size() < 2) {
|
||||
if (funcs.size() < 1 && metafunctable.size() < 2) {
|
||||
return;
|
||||
}
|
||||
// Metamethods directly on the metatable itself
|
||||
|
@ -102,28 +102,13 @@ namespace sol {
|
|||
// prevents calling new/GC on pointer-based tables.
|
||||
luaL_Reg& oldref = metafunctable[metafunctable.size() - 3];
|
||||
luaL_Reg old = oldref;
|
||||
luaL_Reg cutoff = { nullptr, nullptr };
|
||||
oldref = cutoff;
|
||||
oldref = { nullptr, nullptr };
|
||||
luaL_setfuncs(L, metafunctable.data(), metaup);
|
||||
oldref = old;
|
||||
}
|
||||
else {
|
||||
luaL_setfuncs(L, metafunctable.data(), metaup);
|
||||
}
|
||||
// Functions accessed by regular calls are put into a table
|
||||
// that get put into the metatable.__index metamethod
|
||||
lua_createtable(L, 0, functable.size());
|
||||
int functableindex = lua_gettop(L);
|
||||
int up = stack::stack_detail::push_upvalues(L, funcs);
|
||||
luaL_setfuncs(L, functable.data(), up);
|
||||
|
||||
// Also, set the variable indexer that's inside of the metatable's index
|
||||
luaL_newmetatable(L, &usertype_traits<T>::variable_metatable[0]);
|
||||
int varup = stack::stack_detail::push_upvalues(L, funcs);
|
||||
luaL_setfuncs(L, varfunctable.data(), varup);
|
||||
lua_setmetatable(L, functableindex);
|
||||
|
||||
lua_setfield(L, metatableindex, "__index");
|
||||
}
|
||||
|
||||
template <typename T, typename Functions>
|
||||
|
@ -139,44 +124,45 @@ namespace sol {
|
|||
// gctable name by default has ♻ part of it
|
||||
lua_setglobal(L, &usertype_traits<T>::gc_table[0]);
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
static void do_constructor(lua_State* L, T* obj, call_syntax syntax, int, types<Args...>) {
|
||||
default_construct fx{};
|
||||
stack::call(types<void>(), types<Args...>(), L, -1 + static_cast<int>(syntax), fx, obj);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void match_constructor(lua_State*, T*, call_syntax, int) {
|
||||
throw error("No matching constructor for the arguments provided");
|
||||
}
|
||||
|
||||
template<typename T, typename ...CArgs, typename... Args>
|
||||
static void match_constructor(lua_State* L, T* obj, call_syntax syntax, int argcount, types<CArgs...> t, Args&&... args) {
|
||||
if (argcount == sizeof...(CArgs)) {
|
||||
do_constructor<T>(L, obj, syntax, argcount, t);
|
||||
return;
|
||||
}
|
||||
match_constructor<T>(L, obj, syntax, argcount, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class usertype {
|
||||
private:
|
||||
typedef std::map<std::string, base_function*> function_map_t;
|
||||
typedef std::map<std::string, std::pair<bool, base_function*>> function_map_t;
|
||||
std::vector<std::string> functionnames;
|
||||
std::vector<std::unique_ptr<base_function>> functions;
|
||||
std::vector<luaL_Reg> functiontable;
|
||||
std::vector<luaL_Reg> metafunctiontable;
|
||||
std::array<luaL_Reg, 3> variablefunctiontable;
|
||||
base_function* indexfunc;
|
||||
base_function* newindexfunc;
|
||||
function_map_t indexwrapper, newindexwrapper;
|
||||
lua_CFunction constructfunc;
|
||||
lua_CFunction destructfunc;
|
||||
base_function* constructfunc;
|
||||
base_function* destructfunc;
|
||||
lua_CFunction functiongc;
|
||||
|
||||
template<typename... TTypes>
|
||||
struct constructor {
|
||||
template<typename... Args>
|
||||
static void do_constructor(lua_State* L, T* obj, call_syntax syntax, int, types<Args...>) {
|
||||
default_construct fx{};
|
||||
stack::call(types<void>(), types<Args...>(), L, -1 + static_cast<int>(syntax), fx, obj);
|
||||
}
|
||||
|
||||
static void match_constructor(lua_State*, T*, call_syntax, int) {
|
||||
throw error("No matching constructor for the arguments provided");
|
||||
}
|
||||
|
||||
template<typename ...CArgs, typename... Args>
|
||||
static void match_constructor(lua_State* L, T* obj, call_syntax syntax, int argcount, types<CArgs...> t, Args&&... args) {
|
||||
if (argcount == sizeof...(CArgs)) {
|
||||
do_constructor(L, obj, syntax, argcount, t);
|
||||
return;
|
||||
}
|
||||
match_constructor(L, obj, syntax, argcount, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
static int construct(lua_State* L) {
|
||||
const auto& meta = usertype_traits<T>::metatable;
|
||||
call_syntax syntax = stack::get_call_syntax(L, meta);
|
||||
|
@ -186,15 +172,16 @@ namespace sol {
|
|||
T*& referencepointer = *pointerpointer;
|
||||
T* obj = reinterpret_cast<T*>(pointerpointer + 1);
|
||||
referencepointer = obj;
|
||||
match_constructor(L, obj, syntax, argcount - static_cast<int>(syntax), identity_t<TTypes>()...);
|
||||
int userdataindex = lua_gettop(L);
|
||||
usertype_detail::match_constructor(L, obj, syntax, argcount - static_cast<int>(syntax), identity_t<TTypes>()...);
|
||||
|
||||
if (luaL_newmetatable(L, &meta[0]) == 1) {
|
||||
lua_pop(L, 1);
|
||||
std::string err = "Unable to get usertype metatable for ";
|
||||
std::string err = "unable to get usertype metatable for ";
|
||||
err += meta;
|
||||
throw error(err);
|
||||
}
|
||||
lua_setmetatable(L, -2);
|
||||
lua_setmetatable(L, userdataindex);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -263,8 +250,8 @@ namespace sol {
|
|||
auto baseptr = make_function(name, std::forward<Fx>(func));
|
||||
functions.emplace_back(std::move(baseptr));
|
||||
if (is_variable::value) {
|
||||
indexwrapper.insert({ name, functions.back().get() });
|
||||
newindexwrapper.insert({ name, functions.back().get() });
|
||||
indexwrapper.insert({ name, { false, functions.back().get() } });
|
||||
newindexwrapper.insert({ name, { false, functions.back().get() } });
|
||||
return;
|
||||
}
|
||||
auto metamethodfind = std::find(meta_function_names.begin(), meta_function_names.end(), name);
|
||||
|
@ -280,10 +267,11 @@ namespace sol {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
metafunctiontable.push_back({ name.c_str(), &base_function::usertype<N>::call });
|
||||
metafunctiontable.push_back({ name.c_str(), detail::usertype_call<N> });
|
||||
return;
|
||||
}
|
||||
functiontable.push_back({ name.c_str(), &base_function::usertype<N>::call });
|
||||
indexwrapper.insert({ name, { true, functions.back().get() } });
|
||||
functiontable.push_back({ name.c_str(), detail::usertype_call<N> });
|
||||
}
|
||||
|
||||
template<std::size_t N, typename Fx, typename... Args>
|
||||
|
@ -304,24 +292,23 @@ namespace sol {
|
|||
int variableend = 0;
|
||||
if (!indexwrapper.empty()) {
|
||||
functions.push_back(std::make_unique<usertype_indexing_function>("__index", indexfunc, std::move(indexwrapper)));
|
||||
variablefunctiontable[0] = { "__index", &base_function::usertype<N>::call };
|
||||
metafunctiontable.push_back({ "__index", detail::usertype_call<N> });
|
||||
++variableend;
|
||||
}
|
||||
if (!newindexwrapper.empty()) {
|
||||
functions.push_back(std::make_unique<usertype_indexing_function>("__newindex", newindexfunc, std::move(newindexwrapper)));
|
||||
variablefunctiontable[variableend] = { "__newindex", indexwrapper.empty() ? &base_function::usertype<N>::call : &base_function::usertype<N + 1>::call };
|
||||
metafunctiontable.push_back({ "__newindex", indexwrapper.empty() ? detail::usertype_call<N> : detail::usertype_call<N + 1> });
|
||||
++variableend;
|
||||
}
|
||||
variablefunctiontable[variableend] = { nullptr, nullptr };
|
||||
switch (variableend) {
|
||||
case 2:
|
||||
destructfunc = &base_function::usertype<N + 2>::gc;
|
||||
functiongc = detail::usertype_gc<N + 2>;
|
||||
break;
|
||||
case 1:
|
||||
destructfunc = &base_function::usertype<N + 1>::gc;
|
||||
functiongc = detail::usertype_gc<N + 1>;
|
||||
break;
|
||||
case 0:
|
||||
destructfunc = &base_function::usertype<N + 0>::gc;
|
||||
functiongc = detail::usertype_gc<N + 0>;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -332,10 +319,10 @@ namespace sol {
|
|||
|
||||
template<typename... Args, typename... CArgs>
|
||||
usertype(constructors<CArgs...>, Args&&... args)
|
||||
: indexfunc(nullptr), newindexfunc(nullptr), constructfunc(nullptr), destructfunc(nullptr) {
|
||||
functionnames.reserve(sizeof...(args)+2);
|
||||
functiontable.reserve(sizeof...(args));
|
||||
metafunctiontable.reserve(sizeof...(args));
|
||||
: indexfunc(nullptr), newindexfunc(nullptr), constructfunc(nullptr), destructfunc(nullptr), functiongc(nullptr) {
|
||||
functionnames.reserve(sizeof...(args)+3);
|
||||
functiontable.reserve(sizeof...(args)+3);
|
||||
metafunctiontable.reserve(sizeof...(args)+3);
|
||||
|
||||
build_function_tables<0>(std::forward<Args>(args)...);
|
||||
|
||||
|
@ -348,20 +335,20 @@ namespace sol {
|
|||
// as all pointered types are considered
|
||||
// to be references. This makes returns of
|
||||
// `std::vector<int>&` and `std::vector<int>*` work
|
||||
functiontable.push_back({ nullptr, nullptr });
|
||||
metafunctiontable.push_back({ nullptr, nullptr });
|
||||
functiontable.push_back({ nullptr, nullptr });
|
||||
}
|
||||
|
||||
int push(lua_State* L) {
|
||||
// push pointer tables first,
|
||||
usertype_detail::push_metatable<T*>(L, functions, functiontable, metafunctiontable, variablefunctiontable);
|
||||
usertype_detail::push_metatable<T*>(L, functions, functiontable, metafunctiontable);
|
||||
lua_pop(L, 1);
|
||||
// but leave the regular T table on last
|
||||
// so it can be linked to a type for usage with `.new(...)` or `:new(...)`
|
||||
usertype_detail::push_metatable<T>(L, functions, functiontable, metafunctiontable, variablefunctiontable);
|
||||
usertype_detail::push_metatable<T>(L, functions, functiontable, metafunctiontable);
|
||||
// Make sure to drop a table in the global namespace to properly destroy the pushed functions
|
||||
// at some later point in life
|
||||
usertype_detail::set_global_deleter<T>(L, destructfunc, functions);
|
||||
usertype_detail::set_global_deleter<T>(L, functiongc, functions);
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1034,14 +1034,17 @@ TEST_CASE("usertype/member-variables", "allow table-like accessors to behave as
|
|||
REQUIRE_NOTHROW(lua.script("v = Vec.new(1, 2, 3)\n"
|
||||
"v2 = Vec.new(0, 1, 0)\n"
|
||||
"print(v:length())\n"
|
||||
"v.x = 2\n"
|
||||
));
|
||||
REQUIRE_NOTHROW(lua.script("v.x = 2\n"
|
||||
"v2.y = 2\n"
|
||||
"print(v.x, v.y, v.z)\n"
|
||||
"print(v2.x, v2.y, v2.z)\n"
|
||||
"assert(v.x == 2)\n"
|
||||
));
|
||||
REQUIRE_NOTHROW(lua.script("assert(v.x == 2)\n"
|
||||
"assert(v2.x == 0)\n"
|
||||
"assert(v2.y == 2)\n"
|
||||
"v.x = 3\n"
|
||||
));
|
||||
REQUIRE_NOTHROW(lua.script("v.x = 3\n"
|
||||
"local x = v.x\n"
|
||||
"assert(x == 3)\n"
|
||||
));
|
||||
|
|
Loading…
Reference in New Issue
Block a user