overloading searching improvements and general refactoring for cleaner implementation.

This commit is contained in:
ThePhD 2016-02-18 22:17:52 -05:00
parent 0ee92c0142
commit b2b73db5cb
8 changed files with 230 additions and 223 deletions

View File

@ -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);
}

View File

@ -175,8 +175,16 @@ public:
} // detail
struct base_function {
virtual int operator()(lua_State*) {
throw error("failure to call specialized wrapped C++ function from Lua");
}
virtual ~base_function() {}
};
namespace detail {
static int base_call(lua_State* L, void* inheritancedata) {
if(inheritancedata == nullptr) {
if (inheritancedata == nullptr) {
throw error("call from Lua to C++ function has null data");
}
@ -187,7 +195,7 @@ struct base_function {
}
static int base_gc(lua_State*, void* udata) {
if(udata == nullptr) {
if (udata == nullptr) {
throw error("call from lua to C++ gc function with null data");
}
@ -197,32 +205,15 @@ struct base_function {
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*) {
static void func_gc(std::true_type, lua_State*) {
}
template <std::size_t limit>
static void func_gc (std::false_type, lua_State* L) {
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) {
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{};
@ -231,18 +222,28 @@ struct base_function {
}
}
static int gc(lua_State* L) {
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;
}
};
virtual int operator()(lua_State*) {
throw error("failure to call specialized wrapped C++ function from Lua");
}
virtual ~base_function() {}
};
}
} // sol

View File

@ -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);
}
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);
}
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);
}
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

View File

@ -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,16 +140,22 @@ 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);

View File

@ -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);
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

View File

@ -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;

View File

@ -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>
class usertype {
private:
typedef std::map<std::string, 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;
template<typename... TTypes>
struct constructor {
template<typename... Args>
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 ...CArgs, typename... Args>
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(L, obj, syntax, argcount, t);
do_constructor<T>(L, obj, syntax, argcount, t);
return;
}
match_constructor(L, obj, syntax, argcount, std::forward<Args>(args)...);
match_constructor<T>(L, obj, syntax, argcount, std::forward<Args>(args)...);
}
}
template<typename T>
class usertype {
private:
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;
base_function* indexfunc;
base_function* newindexfunc;
function_map_t indexwrapper, newindexwrapper;
base_function* constructfunc;
base_function* destructfunc;
lua_CFunction functiongc;
template<typename... TTypes>
struct constructor {
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;
}
};

View File

@ -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"
));