From eb25bb05bb9b00215100488b573d61dacaa3a844 Mon Sep 17 00:00:00 2001 From: ThePhD Date: Sat, 28 Jun 2014 23:16:48 -0700 Subject: [PATCH] Overloaded functions now work properly when types are specified in signature this triggered overhaul of set_function/pusher::push(...) both state and table reflect changes to userdata structure to make it easier to use tests updated to account for overload resolution some function-related traits added to make use easier -- cleaned up archaic typenames in function_types.hpp Account for std::reference_wrapper for objects -- sol now uses copy-by-default (value-semantics) for all functors updated tests to reflect this --- sol/function.hpp | 139 +++++++++++++++++++------------- sol/function_types.hpp | 30 +++---- sol/stack.hpp | 9 ++- sol/state.hpp | 53 ++++++++---- sol/table.hpp | 177 +++++++++++++---------------------------- sol/traits.hpp | 59 +++++++++++--- sol/types.hpp | 6 +- sol/userdata.hpp | 144 +++++++++++++-------------------- tests.cpp | 68 +++++++++++++--- 9 files changed, 364 insertions(+), 321 deletions(-) diff --git a/sol/function.hpp b/sol/function.hpp index d81578c7..0edbf854 100644 --- a/sol/function.hpp +++ b/sol/function.hpp @@ -85,76 +85,112 @@ public: }; namespace stack { -template <> -struct pusher { +template +struct pusher> { - template - static void set_isfunction_fx(std::true_type, lua_State* L, TFx&& fx) { - set_fx(std::false_type(), L, std::forward(fx)); + template::type> + static void set_memfx(types t, lua_State* L, Fx&& fx) { + typedef Decay> raw_fx_t; + typedef R(* fx_ptr_t)(Args...); + typedef std::is_convertible is_convertible; + set_isconvertible_fx(is_convertible(), t, L, std::forward(fx)); } - template - static void set_isfunction_fx(std::false_type, lua_State* L, TFx&& fx) { - typedef Decay clean_lambda; - typedef typename function_traits::free_function_pointer_type raw_func_t; - typedef std::is_convertible is_convertible; - set_isconvertible_fx(is_convertible(), L, std::forward(fx)); + template::type> + static void set_memfx(types, lua_State* L, Fx&& fx){ + set_memfx(types(), L, std::forward(fx)); } - template - static void set_isconvertible_fx(std::true_type, lua_State* L, TFx&& fx) { - typedef Decay clean_lambda; - typedef typename function_traits::free_function_pointer_type raw_func_t; - set_isfunction_fx(std::true_type(), L, raw_func_t(std::forward(fx))); + template + static void set_memfx(types<>, lua_State* L, Fx&& fx) { + typedef Unqualified> fx_t; + typedef decltype(&fx_t::operator()) Sig; + set_memfx(types>(), L, std::forward(fx)); } - template - static void set_isconvertible_fx(std::false_type, lua_State* L, TFx&& fx) { - typedef typename std::remove_pointer>::type clean_fx; - std::unique_ptr sptr(new functor_function(std::forward(fx))); - set_fx(L, std::move(sptr)); + template + static void set(lua_State* L, R fxptr(Args...)){ + set_fx(std::false_type(), L, fxptr); } - template - static void set_lvalue_fx(std::true_type, lua_State* L, TFx&& fx, TObj&& obj) { - set_fx(std::true_type(), L, std::forward(fx), std::forward(obj)); + template + static void set(lua_State* L, Sig* fxptr){ + set_fx(std::false_type(), L, fxptr); } - template - static void set_lvalue_fx(std::false_type, lua_State* L, TFx&& fx, TObj&& obj) { - typedef typename std::remove_pointer>::type clean_fx; - std::unique_ptr sptr(new member_function(std::forward(obj), std::forward(fx))); - return set_fx(L, std::move(sptr)); + template + static void set(lua_State* L, R (C::*memfxptr)(Args...), T&& obj) { + typedef Bool::value || std::is_pointer::value> is_reference; + set_reference_fx(is_reference(), L, memfxptr, std::forward(obj)); } - template - static void set_fx(std::true_type, lua_State* L, TFx&& fx, TObj&& obj) { + template + static void set(lua_State* L, Sig C::* memfxptr, T&& obj) { + typedef Bool::value || std::is_pointer::value> is_reference; + set_reference_fx(is_reference(), L, memfxptr, std::forward(obj)); + } + + template + static void set(lua_State* L, Fx&& fx) { + set_memfx(types(), L, std::forward(fx)); + } + + template + static void set_isconvertible_fx(std::true_type, types, lua_State* L, Fx&& fx) { + typedef R(* fx_ptr_t)(Args...); + fx_ptr_t fxptr = unwrapper(std::forward(fx)); + set(L, fxptr); + } + + template + static void set_isconvertible_fx(std::false_type, types, lua_State* L, Fx&& fx) { + typedef Decay> fx_t; + std::unique_ptr sptr(new functor_function(std::forward(fx))); + set_fx(L, std::move(sptr)); + } + + template + static void set_reference_fx(std::true_type, lua_State* L, Fx&& fx, T&& obj) { + set_fx(std::true_type(), L, std::forward(fx), std::forward(obj)); + } + + template + static void set_reference_fx(std::false_type, lua_State* L, Fx&& fx, T&& obj) { + typedef typename std::remove_pointer>::type clean_fx; + std::unique_ptr sptr(new member_function(std::forward(obj), std::forward(fx))); + return set_fx(L, std::move(sptr)); + } + + template + static void set_fx(std::true_type, lua_State* L, Fx&& fx, T&& obj) { // Layout: // idx 1...n: verbatim data of member function pointer // idx n + 1: is the object's void pointer // We don't need to store the size, because the other side is templated // with the same member function pointer type - Decay fxptr(std::forward(fx)); - void* userobjdata = static_cast(sol::detail::get_ptr(obj)); - lua_CFunction freefunc = &static_member_function, TFx>::call; + Decay memfxptr(std::forward(fx)); + auto userptr = sol::detail::get_ptr(obj); + void* userobjdata = static_cast(userptr); + lua_CFunction freefunc = &static_member_function, Fx>::call; - int upvalues = stack::detail::push_as_upvalues(L, fxptr); + int upvalues = stack::detail::push_as_upvalues(L, memfxptr); stack::push(L, userobjdata); - stack::pusher{}.push(L, freefunc, upvalues); + ++upvalues; + stack::push(L, freefunc, upvalues); } - template - static void set_fx(lua_State* L, std::false_type, TFx&& fx) { - Decay target(std::forward(fx)); - lua_CFunction freefunc = &static_function::call; + template + static void set_fx(std::false_type, lua_State* L, Fx&& fx) { + Decay target(std::forward(fx)); + lua_CFunction freefunc = &static_function::call; int upvalues = stack::detail::push_as_upvalues(L, target); - stack::pusher{}.push(L, freefunc, upvalues); + stack::push(L, freefunc, upvalues); } - template + template static void set_fx(lua_State* L, std::unique_ptr luafunc) { - auto&& metakey = userdata_traits>::metatable; + auto&& metakey = userdata_traits>::metatable; const char* metatablename = std::addressof(metakey[0]); base_function* target = luafunc.release(); void* userdata = reinterpret_cast(target); @@ -164,27 +200,16 @@ struct pusher { lua_pushstring(L, "__gc"); stack::push(L, &base_function::gc); lua_settable(L, -3); + lua_pop(L, 1); } stack::detail::push_userdata(L, metatablename, userdata); - stack::pusher{}.push(L, freefunc, 1); - } - - template - static void set_function(lua_State* L, TFx&& fx) { - typedef typename std::remove_pointer>::type clean_fx; - set_isfunction_fx(std::is_function(), L, std::forward(fx)); - } - - template - static void set_function(lua_State* L, TFx&& fx, TObj&& obj) { - set_lvalue_fx(Bool::value || std::is_pointer::value>(), - L, std::forward(fx), std::forward(obj)); + stack::push(L, freefunc, 1); } template static void push(lua_State* L, Args&&... args) { - set_function(L, std::forward(args)...); + set(L, std::forward(args)...); } }; diff --git a/sol/function_types.hpp b/sol/function_types.hpp index fb6b79b7..229e29ed 100644 --- a/sol/function_types.hpp +++ b/sol/function_types.hpp @@ -208,7 +208,8 @@ struct base_function { template struct functor_function : public base_function { typedef decltype(&Function::operator()) function_type; - typedef function_traits traits_type; + typedef function_return_t return_type; + typedef function_args_t args_type; Function fx; template @@ -229,7 +230,6 @@ struct functor_function : public base_function { template int operator()(types, types t, lua_State* L) { - typedef typename return_type::type return_type; return_type r = stack::get_call(L, fx, t); std::ptrdiff_t nargs = sizeof...(Args); lua_pop(L, nargs); @@ -238,29 +238,30 @@ struct functor_function : public base_function { } virtual int operator()(lua_State* L) override { - return (*this)(tuple_types(), typename traits_type::args_type(), L); + return (*this)(tuple_types(), args_type(), L); } }; template struct member_function : public base_function { - typedef typename std::remove_pointer::type>::type function_type; - typedef function_traits traits_type; + typedef typename std::remove_pointer>::type function_type; + typedef function_return_t return_type; + typedef function_args_t args_type; struct functor { T member; function_type invocation; - template - functor(T m, FxArgs&&... fxargs): member(std::move(m)), invocation(std::forward(fxargs)...) {} + template + functor(Tm&& m, FxArgs&&... fxargs): member(std::forward(m)), invocation(std::forward(fxargs)...) {} template - typename traits_type::return_type operator()(Args&&... args) { + return_type operator()(Args&&... args) { return (member.*invocation)(std::forward(args)...); } } fx; - template - member_function(T m, FxArgs&&... fxargs): fx(std::move(m), std::forward(fxargs)...) {} + template + member_function(Tm&& m, FxArgs&&... fxargs): fx(std::forward(m), std::forward(fxargs)...) {} template int operator()(types, types t, lua_State* L) { @@ -275,7 +276,6 @@ struct member_function : public base_function { template int operator()(types, types t, lua_State* L) { - typedef typename return_type::type return_type; return_type r = stack::get_call(L, fx, t); std::ptrdiff_t nargs = sizeof...(Args); lua_pop(L, nargs); @@ -284,7 +284,7 @@ struct member_function : public base_function { } virtual int operator()(lua_State* L) override { - return (*this)(tuple_types(), typename traits_type::args_type(), L); + return (*this)(tuple_types(), args_type(), L); } }; @@ -292,8 +292,8 @@ template struct userdata_function : public base_function { typedef typename std::remove_pointer::type T; typedef typename std::remove_pointer::type>::type function_type; - typedef function_traits traits_type; - typedef typename traits_type::return_type return_type; + typedef function_args_t args_type; + typedef function_return_t return_type; detail::functor fx; @@ -347,7 +347,7 @@ struct userdata_function : public base_function { fx.item = detail::get_ptr(stack::get(L, 1)); if (fx.item == nullptr) throw error("userdata for function call is null: are you using wrong call syntax? (use item:function(...) synax)"); - return (*this)(tuple_types(), typename traits_type::args_type(), L); + return (*this)(tuple_types(), args_type(), L); } }; diff --git a/sol/stack.hpp b/sol/stack.hpp index 884966b0..e1bcf429 100644 --- a/sol/stack.hpp +++ b/sol/stack.hpp @@ -39,6 +39,11 @@ inline T* get_ptr(T& val) { return std::addressof(val); } +template +inline T* get_ptr(std::reference_wrapper val) { + return std::addressof(val.get()); +} + template inline T* get_ptr(T* val) { return val; @@ -310,8 +315,8 @@ inline void push(lua_State* L, T&& t, Args&&... args) { // overload allows to use a pusher of a specific type, but pass in any kind of args template -inline void push(lua_State* L, Arg&& t, Args&&... args) { - pusher>{}.push(L, std::forward(t), std::forward(args)...); +inline void push(lua_State* L, Arg&& arg, Args&&... args) { + pusher>{}.push(L, std::forward(arg), std::forward(args)...); } inline void push_args(lua_State*) { diff --git a/sol/state.hpp b/sol/state.hpp index 2375d727..024f16ab 100644 --- a/sol/state.hpp +++ b/sol/state.hpp @@ -61,6 +61,10 @@ public: lua_atpanic(L.get(), detail::atpanic); } + lua_State* lua_state() const { + return L.get(); + } + template void open_libraries(Args&&... args) { static_assert(are_same::value, "all types must be libraries"); @@ -133,21 +137,14 @@ public: return *this; } - template - state& set_function(T&& key, TFx&& fx) { - global.set_function(std::forward(key), std::forward(fx)); - return *this; - } - - template - state& set_function(T&& key, TFx&& fx, TObj&& obj) { - global.set_function(std::forward(key), std::forward(fx), std::forward(obj)); - return *this; - } - template state& set_userdata(userdata& user) { - global.set_userdata(user); + return set_userdata(user.name(), user); + } + + template + state& set_userdata(Key&& key, userdata& user) { + global.set_userdata(std::forward(key), user); return *this; } @@ -198,8 +195,34 @@ public: return global[std::forward(key)]; } - lua_State* lua_state() const { - return L.get(); + template + state& set_function(Key&& key, R fun_ptr(Args...)){ + global.set_function(std::forward(key), fun_ptr); + return *this; + } + + template + state& set_function(Key&& key, Sig* fun_ptr){ + global.set_function(std::forward(key), fun_ptr); + return *this; + } + + template + state& set_function(Key&& key, R (C::*mem_ptr)(Args...), T&& obj) { + global.set_function(std::forward(key), mem_ptr, std::forward(obj)); + return *this; + } + + template + state& set_function(Key&& key, Sig C::* mem_ptr, T&& obj) { + global.set_function(std::forward(key), mem_ptr, std::forward(obj)); + return *this; + } + + template + state& set_function(Key&& key, Fx&& fx) { + global.set_function(std::forward(key), std::forward(fx)); + return *this; } }; } // sol diff --git a/sol/table.hpp b/sol/table.hpp index b739ed26..3506f3a2 100644 --- a/sol/table.hpp +++ b/sol/table.hpp @@ -83,23 +83,16 @@ public: return *this; } - template - table& set_function(T&& key, TFx&& fx) { - typedef typename std::remove_pointer>::type clean_fx; - return set_isfunction_fx(std::is_function(), std::forward(key), std::forward(fx)); - } - - template - table& set_function(T&& key, TFx&& fx, TObj&& obj) { - return set_lvalue_fx(Bool::value || std::is_pointer::value>(), - std::forward(key), std::forward(fx), std::forward(obj)); - } - template - table& set_userdata(userdata& user) { - stack::push(state(), user); - lua_setglobal(state(), user.name().c_str()); + table& set_userdata(userdata& user) { + return set_userdata(user.name(), user); + } + template + table& set_userdata(Key&& key, userdata& user) { + std::string ukey(std::forward(key)); + stack::push(state(), user); + lua_setglobal(state(), ukey.c_str()); return *this; } @@ -121,121 +114,63 @@ public: void pop(int n = 1) const noexcept { lua_pop(state(), n); } + + template + table& set_function(Key&& key, R fun_ptr(Args...)){ + set_resolved_function(std::forward(key), fun_ptr); + return *this; + } + + template + table& set_function(Key&& key, Sig* fun_ptr){ + set_resolved_function(std::forward(key), fun_ptr); + return *this; + } + + template + table& set_function(Key&& key, R (C::*mem_ptr)(Args...), T&& obj) { + set_resolved_function(std::forward(key), mem_ptr, std::forward(obj)); + return *this; + } + + template + table& set_function(Key&& key, Sig C::* mem_ptr, T&& obj) { + set_resolved_function(std::forward(key), mem_ptr, std::forward(obj)); + return *this; + } + + template + table& set_function(Key&& key, Fx&& fx) { + set_fx(types(), std::forward(key), std::forward(fx)); + return *this; + } + private: - template - table& set_isfunction_fx(std::true_type, T&& key, TFx&& fx) { - return set_fx(std::false_type(), std::forward(key), std::forward(fx)); + template::type> + void set_fx(types, Key&& key, Fx&& fx) { + set_resolved_function(std::forward(key), std::forward(fx)); } - template - table& set_isfunction_fx(std::false_type, T&& key, TFx&& fx) { - typedef Decay clean_lambda; - typedef typename function_traits::free_function_pointer_type raw_func_t; - typedef std::is_convertible isconvertible; - return set_isconvertible_fx(isconvertible(), std::forward(key), std::forward(fx)); + template::type> + void set_fx(types, Key&& key, Fx&& fx){ + set_fx(types(), std::forward(key), std::forward(fx)); } - template - table& set_isconvertible_fx(std::true_type, T&& key, TFx&& fx) { - typedef Decay clean_lambda; - typedef typename function_traits::free_function_pointer_type raw_func_t; - return set_isfunction_fx(std::true_type(), std::forward(key), raw_func_t(std::forward(fx))); + template + void set_fx(types<>, Key&& key, Fx&& fx) { + typedef Unqualified> fx_t; + typedef decltype(&fx_t::operator()) Sig; + set_fx(types>(), std::forward(key), std::forward(fx)); } - template - table& set_isconvertible_fx(std::false_type, T&& key, TFx&& fx) { - typedef typename std::remove_pointer>::type clean_fx; - std::unique_ptr sptr(new functor_function(std::forward(fx))); - return set_fx(std::forward(key), std::move(sptr)); - } - - template - table& set_lvalue_fx(std::true_type, T&& key, TFx&& fx, TObj&& obj) { - return set_fx(std::true_type(), std::forward(key), std::forward(fx), std::forward(obj)); - } - - template - table& set_lvalue_fx(std::false_type, T&& key, TFx&& fx, TObj&& obj) { - typedef typename std::remove_pointer>::type clean_fx; - std::unique_ptr sptr(new member_function(std::forward(obj), std::forward(fx))); - return set_fx(std::forward(key), std::move(sptr)); - } - - template - table& set_fx(std::true_type, T&& key, TFx&& fx, TObj&& obj) { - std::string fkey(key); - - // Layout: - // idx 1...n: verbatim data of member function pointer - // idx n + 1: is the object's void pointer - // We don't need to store the size, because the other side is templated - // with the same member function pointer type - Decay fxptr(std::forward(fx)); - void* userobjdata = static_cast(detail::get_ptr(obj)); - lua_CFunction freefunc = &static_member_function, TFx>::call; - const char* freefuncname = fkey.c_str(); - const luaL_Reg funcreg[2] = { - { freefuncname, freefunc }, - { nullptr, nullptr } - }; - - + template + void set_resolved_function(Key&& key, Args&&... args) { + std::string fkey(std::forward(key)); push(); - - int upvalues = stack::detail::push_as_upvalues(state(), fxptr); - stack::push(state(), userobjdata); - luaL_setfuncs(state(), funcreg, upvalues + 1); - + int tabletarget = lua_gettop(state()); + stack::push>(state(), std::forward(args)...); + lua_setfield(state(), tabletarget, fkey.c_str()); pop(); - return *this; - } - - template - table& set_fx(std::false_type, T&& key, TFx&& fx) { - std::string fkey(key); - Decay target(std::forward(fx)); - lua_CFunction freefunc = &static_function::call; - const char* freefuncname = fkey.c_str(); - const luaL_Reg funcreg[2] = { - { freefuncname, freefunc }, - { nullptr, nullptr } - }; - - push(); - int upvalues = stack::detail::push_as_upvalues(state(), target); - luaL_setfuncs(state(), funcreg, upvalues); - pop(); - - return *this; - } - - template - table& set_fx(T&& key, std::unique_ptr luafunc) { - std::string fkey(key); - std::string metakey("sol.stateful."); - metakey += fkey; - metakey += ".meta"; - base_function* target = luafunc.release(); - void* userdata = reinterpret_cast(target); - lua_CFunction freefunc = &base_function::call; - const char* freefuncname = fkey.c_str(); - const char* metatablename = metakey.c_str(); - const luaL_Reg funcreg[2] = { - { freefuncname, freefunc }, - { nullptr, nullptr } - }; - - if (luaL_newmetatable(state(), metatablename) == 1) { - lua_pushstring(state(), "__gc"); - lua_pushcclosure(state(), &base_function::gc, 0); - lua_settable(state(), -3); - } - - push(); - stack::detail::push_userdata(state(), metatablename, userdata); - luaL_setfuncs(state(), funcreg, 1); - pop(); - return *this; } }; } // sol diff --git a/sol/traits.hpp b/sol/traits.hpp index 3de0af90..059774bb 100644 --- a/sol/traits.hpp +++ b/sol/traits.hpp @@ -24,11 +24,33 @@ #include "tuple.hpp" #include +#include namespace sol { template struct identity { typedef T type; }; +template +struct is_tuple : std::false_type{ }; + +template +struct is_tuple> : std::true_type{ }; + +template +struct unwrap { + typedef T type; +}; + +template +struct unwrap> { + typedef typename std::add_lvalue_reference::type type; +}; + +template class Templ> +struct is_specialization_of : std::false_type { }; +template class Templ> +struct is_specialization_of, Templ> : std::true_type { }; + template struct are_same : std::true_type { }; @@ -68,6 +90,9 @@ using Unqualified = typename std::remove_cv::t template using Decay = typename std::decay::type; +template +using Unwrap = typename unwrap::type; + template struct return_type { typedef std::tuple type; @@ -83,17 +108,6 @@ struct return_type<> : types<>{ typedef void type; }; -template -struct is_tuple : std::false_type{ }; - -template -struct is_tuple> : std::true_type{ }; - -template class Templ> -struct is_specialization_of : std::false_type { }; -template class Templ> -struct is_specialization_of, Templ> : std::true_type { }; - namespace detail { template>::value> struct is_function_impl : std::is_function::type> {}; @@ -123,6 +137,15 @@ struct Function : Bool::value> {}; template struct function_traits; +template +using function_args_t = typename function_traits::args_type; + +template +using function_signature_t = typename function_traits::signature_type; + +template +using function_return_t = typename function_traits::return_type; + template struct function_traits { static const std::size_t arity = sizeof...(Args); @@ -133,6 +156,7 @@ struct function_traits { typedef typename std::remove_pointer::type function_type; typedef R(*free_function_pointer_type)(Args...); typedef R return_type; + typedef typename std::remove_pointer::type signature_type; template using arg = typename std::tuple_element::type; }; @@ -147,6 +171,7 @@ struct function_traits { typedef typename std::remove_pointer::type function_type; typedef R(*free_function_pointer_type)(Args...); typedef R return_type; + typedef typename std::remove_pointer::type signature_type; template using arg = typename std::tuple_element::type; }; @@ -161,6 +186,7 @@ struct function_traits { typedef R(*function_pointer_type)(Args...); typedef R(*free_function_pointer_type)(Args...); typedef R return_type; + typedef typename std::remove_pointer::type signature_type; template using arg = typename std::tuple_element::type; }; @@ -175,6 +201,7 @@ struct function_traits { typedef R(*function_pointer_type)(Args...); typedef R(*free_function_pointer_type)(Args...); typedef R return_type; + typedef typename std::remove_pointer::type signature_type; template using arg = typename std::tuple_element::type; }; @@ -205,6 +232,16 @@ struct has_key_value_pair_impl { template struct has_key_value_pair : decltype(has_key_value_pair_impl::test(0)) {}; + +template +auto unwrapper(T&& item) -> decltype(std::forward(item)) { + return std::forward(item); +} + +template +Unwrap unwrapper(std::reference_wrapper arg) { + return arg.get(); +} } // sol #endif // SOL_TRAITS_HPP diff --git a/sol/types.hpp b/sol/types.hpp index dd644db9..45c21467 100644 --- a/sol/types.hpp +++ b/sol/types.hpp @@ -31,7 +31,11 @@ struct nil_t {}; const nil_t nil {}; struct void_type {}; const void_type Void {}; -struct function_t {}; + +template +struct function_sig_t {}; +using function_t = function_sig_t<>; + struct upvalue_t { void* value; upvalue_t(void* data) : value(data) {} diff --git a/sol/userdata.hpp b/sol/userdata.hpp index 42cd9b7b..6d18a7e4 100644 --- a/sol/userdata.hpp +++ b/sol/userdata.hpp @@ -108,6 +108,36 @@ private: } }; + template + static int push_upvalues (lua_State* L, TCont&& cont) { + int n = 0; + for (auto& c : cont) { + if (release) + stack::push(L, c.release()); + else + stack::push(L, c.get()); + ++n; + } + return n; + } + + template + static void push_metatable(lua_State* L, Meta&& meta, Funcs&& funcs, FuncTable&& functable, MetaFuncs&& metafuncs, MetaFuncTable&& metafunctable) { + luaL_newmetatable(L, std::addressof(meta[0])); + if (functable.size() > 1) { + // regular functions accessed through __index semantics + int up = push_upvalues(L, funcs); + luaL_setfuncs(L, functable.data(), up); + } + if (metafunctable.size() > 1) { + // meta functions + int up = push_upvalues(L, metafuncs); + luaL_setfuncs(L, metafunctable.data(), up); + } + lua_pushvalue(L, -1); + lua_setfield(L, -1, "__index"); + } + template void build_function_tables() {} @@ -177,49 +207,33 @@ public: userdata(const char* name, constructors c, Args&&... args) : userdata(std::string(name), std::move(c), std::forward(args)...) {} - std::vector& function_names () { - return functionnames; - } - - std::vector>& functions () { - return funcs; - } - - std::vector>& reference_functions () { - return ptrfuncs; - } - - std::vector>& meta_functions () { - return metafuncs; - } - - std::vector>& meta_reference_functions () { - return ptrmetafuncs; - } - - std::vector& function_table () { - return functiontable; - } - - std::vector& reference_function_table () { - return ptrfunctiontable; - } - - std::vector& meta_function_table () { - return metafunctiontable; - } - - std::vector& meta_reference_function_table () { - return ptrmetafunctiontable; - } - - lua_CFunction cleanup_function () { - return cleanup; - } - const std::string& name () const { return luaname; } + + void push (lua_State* L) { + // push pointer tables first, + // but leave the regular T table on last so it can be linked to a type for usage with `.new(...)` + push_metatable(L, userdata_traits::metatable, + ptrfuncs, ptrfunctiontable, + ptrmetafuncs, ptrmetafunctiontable); + push_metatable(L, userdata_traits::metatable, + funcs, functiontable, + metafuncs, metafunctiontable); + // Automatic deleter table -- stays alive until lua VM dies + // even if the user calls collectgarbage() + lua_createtable(L, 0, 0); + lua_createtable(L, 0, 1); + int up = push_upvalues(L, funcs); + up += push_upvalues(L, ptrfuncs); + up += push_upvalues(L, metafuncs); + up += push_upvalues(L, ptrmetafuncs); + lua_pushcclosure(L, cleanup, up); + lua_setfield(L, -2, "__gc"); + lua_setmetatable(L, -2); + // gctable name by default has ♻ part of it + lua_setglobal(L, std::addressof(userdata_traits::gctable[0])); + } }; template @@ -248,56 +262,8 @@ const std::array userdata::metafunctionnames = { namespace stack { template struct pusher> { - template - static int push_upvalues (lua_State* L, TCont&& cont) { - int n = 0; - for (auto& c : cont) { - if (release) - stack::push(L, c.release()); - else - stack::push(L, c.get()); - ++n; - } - return n; - } - - template - static void push_metatable(lua_State* L, Meta&& meta, Funcs&& funcs, FuncTable&& functable, MetaFuncs&& metafuncs, MetaFuncTable&& metafunctable) { - luaL_newmetatable(L, std::addressof(meta[0])); - if (functable.size() > 1) { - // regular functions accessed through __index semantics - int up = push_upvalues(L, funcs); - luaL_setfuncs(L, functable.data(), up); - } - if (metafunctable.size() > 1) { - // meta functions - int up = push_upvalues(L, metafuncs); - luaL_setfuncs(L, metafunctable.data(), up); - } - lua_pushvalue(L, -1); - lua_setfield(L, -1, "__index"); - } - static void push (lua_State* L, userdata& user) { - // push pointer tables first, - // but leave the regular T table on last so it can be linked to a type for usage with `.new(...)` - push_metatable(L, userdata_traits::metatable, user.reference_functions(), user.reference_function_table(), user.meta_reference_functions(), user.meta_reference_function_table()); - push_metatable(L, userdata_traits::metatable, user.functions(), user.function_table(), user.meta_functions(), user.meta_function_table()); - - // Automatic deleter table -- stays alive until lua VM dies - // even if the user calls collectgarbage() - auto cleanup = user.cleanup_function(); - lua_createtable(L, 0, 0); - lua_createtable(L, 0, 1); - int up = push_upvalues(L, user.functions()); - up += push_upvalues(L, user.reference_functions()); - up += push_upvalues(L, user.meta_functions()); - up += push_upvalues(L, user.meta_reference_functions()); - lua_pushcclosure(L, cleanup, up); - lua_setfield(L, -2, "__gc"); - lua_setmetatable(L, -2); - // gctable name by default has ♻ part of it - lua_setglobal(L, std::addressof(userdata_traits::gctable[0])); + user.push(L); } }; } // stack diff --git a/tests.cpp b/tests.cpp index 3b13444b..7df28912 100644 --- a/tests.cpp +++ b/tests.cpp @@ -31,6 +31,26 @@ std::string free_function() { return "test"; } +int overloaded(int x) { + std::cout << x << std::endl; + return 3; +} + +int overloaded(int x, int y) { + std::cout << x << " " << y << std::endl; + return 7; +} + +int overloaded(int x, int y, int z) { + std::cout << x << " " << y << " " << z << std::endl; + return 11; +} + +int non_overloaded(int x, int y, int z) { + std::cout << x << " " << y << " " << z << std::endl; + return 13; +} + std::vector test_table_return_one() { return { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; } @@ -332,25 +352,33 @@ TEST_CASE("tables/functions_variables", "Check if tables and function calls work std::cout << "stateless lambda()" << std::endl; return "test"; } - ); + ); REQUIRE_NOTHROW(run_script(lua)); lua.get("os").set_function("fun", &free_function); REQUIRE_NOTHROW(run_script(lua)); - // l-value, can optimise + // l-value, canNOT optimise + // prefer value semantics unless wrapped with std::reference_wrapper + { auto lval = object(); lua.get("os").set_function("fun", &object::operator(), lval); + } REQUIRE_NOTHROW(run_script(lua)); + auto reflval = object(); + lua.get("os").set_function("fun", &object::operator(), std::ref(reflval)); + REQUIRE_NOTHROW(run_script(lua)); + + // stateful lambda: non-convertible, cannot be optimised int breakit = 50; lua.get("os").set_function("fun", [&breakit] () { - std::cout << "stateless lambda()" << std::endl; - return "test"; - } -); + std::cout << "stateful lambda()" << std::endl; + return "test"; + } + ); REQUIRE_NOTHROW(run_script(lua)); // r-value, cannot optimise @@ -363,6 +391,26 @@ TEST_CASE("tables/functions_variables", "Check if tables and function calls work REQUIRE_NOTHROW(run_script(lua)); } +TEST_CASE("functions/overloaded", "Check if overloaded function resolution templates compile/work") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua.set_function("non_overloaded", non_overloaded); + REQUIRE_NOTHROW(lua.script("x = non_overloaded(1)\nprint(x)")); + + lua.set_function("overloaded", overloaded); + REQUIRE_NOTHROW(lua.script("print(overloaded(1))")); + + lua.set_function("overloaded", overloaded); + REQUIRE_NOTHROW(lua.script("print(overloaded(1, 2))")); + + lua.set_function("overloaded", overloaded); + REQUIRE_NOTHROW(lua.script("print(overloaded(1, 2))")); + + lua.set_function("overloaded", overloaded); + REQUIRE_NOTHROW(lua.script("print(overloaded(1, 2, 3))")); +} + TEST_CASE("functions/return_order_and_multi_get", "Check if return order is in the same reading order specified in Lua") { const static std::tuple triple = std::make_tuple(10, 11, 12); sol::state lua; @@ -730,10 +778,10 @@ TEST_CASE("userdata/lua-stored-userdata", "ensure userdata values can be stored // userdata dies, but still usable in lua! } - lua.script("collectgarbage()\n" + REQUIRE_NOTHROW(lua.script("collectgarbage()\n" "v = Vec.new(1, 2, 3)\n" - "print(v:length())"); + "print(v:length())")); - lua.script("v = Vec.new(1, 2, 3)\n" - "print(v:normalized():length())" ); + REQUIRE_NOTHROW(lua.script("v = Vec.new(1, 2, 3)\n" + "print(v:normalized():length())" )); }