diff --git a/sol/function.hpp b/sol/function.hpp index 31aa3486..9ad46e8f 100644 --- a/sol/function.hpp +++ b/sol/function.hpp @@ -318,12 +318,12 @@ struct pusher> { const static char* metatablename = &metakey[0]; base_function* target = luafunc.release(); void* userdata = reinterpret_cast(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(L, detail::gc); lua_settable(L, -3); lua_pop(L, 1); } diff --git a/sol/function_types_core.hpp b/sol/function_types_core.hpp index edb4c22e..bcaa06f9 100644 --- a/sol/function_types_core.hpp +++ b/sol/function_types_core.hpp @@ -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(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(udata); - std::default_delete dx{}; - dx(ptr); - return 0; - } - - static int call(lua_State* L) { - void** pinheritancedata = static_cast(stack::get(L, 1).value); - return base_call(L, *pinheritancedata); - } - - static int gc(lua_State* L) { - void** pudata = static_cast(stack::get(L, 1).value); - return base_gc(L, *pudata); - } - - template - struct usertype { - static int call(lua_State* L) { - // Zero-based template parameter, but upvalues start at 1 - return base_call(L, stack::get(L, I + 1)); - } - - template - static void func_gc (std::true_type, lua_State*) { - - } - - template - 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(L, static_cast(i + 1)); - base_function* obj = static_cast(up.value); - std::allocator alloc{}; - alloc.destroy(obj); - alloc.deallocate(obj, 1); - } - } - - static int gc(lua_State* L) { - func_gc(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(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(udata); + std::default_delete dx{}; + dx(ptr); + return 0; + } + + template + static void func_gc(std::true_type, lua_State*) { + + } + + template + 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(L, static_cast(i + 1)); + base_function* obj = static_cast(up.value); + std::allocator alloc{}; + alloc.destroy(obj); + alloc.deallocate(obj, 1); + } + } + + inline int call(lua_State* L) { + void** pinheritancedata = static_cast(stack::get(L, 1).value); + return base_call(L, *pinheritancedata); + } + + inline int gc(lua_State* L) { + void** pudata = static_cast(stack::get(L, 1).value); + return base_gc(L, *pudata); + } + + template + inline int usertype_call(lua_State* L) { + // Zero-based template parameter, but upvalues start at 1 + return base_call(L, stack::get(L, I + 1)); + } + + template + inline int usertype_gc(lua_State* L) { + func_gc(Bool<(I < 1)>(), L); + return 0; + } +} + } // sol #endif // SOL_FUNCTION_TYPES_CORE_HPP diff --git a/sol/function_types_overload.hpp b/sol/function_types_overload.hpp index 9f58d75f..a79bebbb 100644 --- a/sol/function_types_overload.hpp +++ b/sol/function_types_overload.hpp @@ -27,108 +27,105 @@ #include "function_types_usertype.hpp" namespace sol { +namespace detail { +template +inline int match_arity(types<>, std::index_sequence<>, std::index_sequence, std::ptrdiff_t, lua_State*, Match&&, int) { + throw error("no matching function call takes this number of arguments and the specified types"); +} + +template +inline int match_arity(types, std::index_sequence, std::index_sequence, std::ptrdiff_t fxarity, lua_State* L, Match&& matchfx, int start) { + typedef function_traits traits; + typedef tuple_types::return_type> return_types; + typedef typename function_traits::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...>::value || traits::arity != fxarity) { + return match_arity(types(), std::index_sequence(), std::index_sequence(), fxarity, L, std::forward(matchfx), start); + } + if (traits::arity != fxarity) { + return match_arity(types(), std::index_sequence(), std::index_sequence(), fxarity, L, std::forward(matchfx), start); + } + if (!detail::check_types(args_type(), args_indices(), L, start)) { + return match_arity(types(), std::index_sequence(), std::index_sequence(), fxarity, L, std::forward(matchfx), start); + } + return matchfx(Index(), fxarity, L, start); +} +} // detail + +template +inline int match(std::ptrdiff_t fxarity, lua_State* L, Match&& matchfx, int start = 1) { + return detail::match_arity(types(), std::index_sequence_for(), std::index_sequence<>(), fxarity, L, std::forward(matchfx), start); +} + +template +inline int match(lua_State* L, Match&& matchfx, int start = 1) { + std::ptrdiff_t fxarity = lua_gettop(L) - (start - 1); + return match(fxarity, L, std::forward(matchfx), start); +} + template struct overloaded_function : base_function { - typedef std::tuple...> overloads_t; - overloads_t overloads; + typedef std::tuple overload_list; + overload_list overloads; + + overloaded_function(overload_set set) + : overloaded_function(indices(), set) {} + + template + overloaded_function(std::index_sequence, overload_set set) + : overloaded_function(std::get(set)...) {} overloaded_function(Functions... fxs) - : overloads({ static_cast(function_traits>::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 - int match_arity(std::index_sequence, 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>, - // std::pair<3-arity, std::tuple>, - // std::pair>, - // ... - //> - auto& package = std::get(overloads); - auto arity = package.first; - if (arity != x) { - return match_arity(std::index_sequence(), L, x); - } - auto& func = package.second; - typedef Unqualified fx_t; - typedef tuple_types::return_type> return_types; - typedef typename function_traits::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(), L, x); - } - return stack::typed_call(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::value>(), L, x); + template + int call(Index, int, lua_State* L, int) { + auto& func = std::get(overloads); + typedef Unqualified Fx; + typedef function_traits traits; + typedef tuple_types::return_type> return_types; + typedef typename function_traits::args_type args_type; + return stack::typed_call(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(args)...); }; + return match(L, mfx); } }; template struct usertype_overloaded_function : base_function { - typedef std::tuple>...> overloads_t; - overloads_t overloads; + typedef std::tuple...> overload_list; + typedef std::index_sequence_for indices; + overload_list overloads; - usertype_overloaded_function(overload_set set) - : usertype_overloaded_function(std::make_index_sequence(), set) {} + usertype_overloaded_function(overload_set set) : usertype_overloaded_function(indices(), set) {} - template - usertype_overloaded_function(std::index_sequence, overload_set set) - : usertype_overloaded_function(std::get(set)...) {} + template + usertype_overloaded_function(std::index_sequence, overload_set set) : usertype_overloaded_function(std::get(set)...) {} + usertype_overloaded_function(Functions... fxs) : overloads(fxs...) {} - usertype_overloaded_function(Functions... fxs) - : overloads({static_cast(function_traits::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 - int match_arity(std::index_sequence, lua_State* L, std::ptrdiff_t x) { - // TODO: - // propogate changes from above down here too when they get figured out - auto& package = std::get(overloads); - auto arity = package.first; - if (arity != x) { - return match_arity(std::index_sequence(), L, x); - } - auto& func = package.second; - typedef Unqualified fx_t; - typedef tuple_types 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(), L, x); - } + template + int call(Index, int, lua_State* L, int) { + auto& func = std::get(overloads); + typedef Unqualified Fx; + typedef typename Fx::traits_type traits; + typedef tuple_types return_types; + typedef typename Fx::args_type args_type; func.item = ptr(stack::get(L, 1)); return stack::typed_call(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::value>(), L, x); + virtual int operator()(lua_State* L) override { + auto mfx = [&](auto&&... args){ return call(std::forward(args)...); }; + return match(L, mfx, 2); } - virtual int operator()(lua_State* L) override { - return match_arity(L); - } }; } // sol diff --git a/sol/function_types_usertype.hpp b/sol/function_types_usertype.hpp index 7567e06d..e0dec933 100644 --- a/sol/function_types_usertype.hpp +++ b/sol/function_types_usertype.hpp @@ -117,11 +117,11 @@ struct usertype_variable_function : public usertype_function_core usertype_variable_function(Args&&... args): base_t(std::forward(args)...) {} int prelude(lua_State* L) { - this->fx.item = ptr(stack::get(L, 1)); + int argcount = lua_gettop(L); + this->fx.item = stack::get(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(*this)(tuple_types(), types<>(), L); @@ -140,17 +140,23 @@ struct usertype_variable_function : public usertype_function_core struct usertype_indexing_function : base_function { std::string name; base_function* original; - std::map functions; + std::map> functions; template usertype_indexing_function(std::string name, base_function* original, Args&&... args): name(std::move(name)), original(original), functions(std::forward(args)...) {} int prelude(lua_State* L) { const char* accessor = stack::get(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& target = functionpair->second; + if (target.first) { + stack::push(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); } diff --git a/sol/stack.hpp b/sol/stack.hpp index e44ab774..96433739 100644 --- a/sol/stack.hpp +++ b/sol/stack.hpp @@ -257,7 +257,8 @@ struct checker { handler(L, index, type::userdata, indextype); return false; } - const type expectedmetatabletype = static_cast(luaL_getmetatable(L, &usertype_traits::metatable[0])); + luaL_getmetatable(L, &usertype_traits::metatable[0]); + const type expectedmetatabletype = get(L); if (expectedmetatabletype == type::nil) { lua_pop(L, 2); handler(L, index, type::userdata, indextype); @@ -316,7 +317,9 @@ struct getter { type t = type_of(L, index); if (t == type::nil) return nullptr; - return std::addressof(getter{}.get(L, index)); + void* udata = lua_touserdata(L, index); + T** obj = static_cast(udata); + return *obj; } }; @@ -470,7 +473,7 @@ struct pusher::value>> { template struct pusher, std::is_signed>::value>> { static int push(lua_State* L, const T& value) { - lua_pushinteger(L, value); + lua_pushinteger(L, static_cast(value)); return 1; } }; @@ -859,12 +862,13 @@ inline int typed_call(types, types ta, Fx&& fx, lua_State* L, i } inline call_syntax get_call_syntax(lua_State* L, const std::string& meta) { - if (get(L, 1) == type::table) { - if (luaL_newmetatable(L, meta.c_str()) == 0) { - lua_settop(L, -2); - return call_syntax::colon; - } + type metatype = stack::get(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 diff --git a/sol/traits.hpp b/sol/traits.hpp index d5c9d79b..5499a4d8 100644 --- a/sol/traits.hpp +++ b/sol/traits.hpp @@ -83,6 +83,9 @@ using Type = typename T::type; template using Bool = std::integral_constant; +template +using Index = std::integral_constant; + template using Not = Bool; @@ -116,6 +119,12 @@ using Unqualified = std::remove_cv_t>; template using Unwrapped = typename unwrapped::type; +template +struct find_in_pack_v : Bool { }; + +template +struct find_in_pack_v : Or, find_in_pack_v> { }; + template struct return_type { typedef std::tuple type; @@ -253,7 +262,7 @@ struct fx_traits { } // detail template -struct function_traits : detail::fx_traits> {}; +struct function_traits : detail::fx_traits> {}; template using function_args_t = typename function_traits::args_type; diff --git a/sol/usertype.hpp b/sol/usertype.hpp index b8d2ca56..c9652c96 100644 --- a/sol/usertype.hpp +++ b/sol/usertype.hpp @@ -88,11 +88,11 @@ namespace sol { }; namespace usertype_detail { - template - inline void push_metatable(lua_State* L, Funcs&& funcs, FuncTable&& functable, MetaFuncTable&& metafunctable, VarFuncTable&& varfunctable) { + template + inline void push_metatable(lua_State* L, Funcs&& funcs, FuncTable&&, MetaFuncTable&& metafunctable) { luaL_newmetatable(L, &usertype_traits::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::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 @@ -139,44 +124,45 @@ namespace sol { // gctable name by default has ♻ part of it lua_setglobal(L, &usertype_traits::gc_table[0]); } + + template + static void do_constructor(lua_State* L, T* obj, call_syntax syntax, int, types) { + default_construct fx{}; + stack::call(types(), types(), L, -1 + static_cast(syntax), fx, obj); + } + + template + static void match_constructor(lua_State*, T*, call_syntax, int) { + throw error("No matching constructor for the arguments provided"); + } + + template + static void match_constructor(lua_State* L, T* obj, call_syntax syntax, int argcount, types t, Args&&... args) { + if (argcount == sizeof...(CArgs)) { + do_constructor(L, obj, syntax, argcount, t); + return; + } + match_constructor(L, obj, syntax, argcount, std::forward(args)...); + } } template class usertype { private: - typedef std::map function_map_t; + typedef std::map> function_map_t; std::vector functionnames; std::vector> functions; std::vector functiontable; std::vector metafunctiontable; - std::array 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 struct constructor { - template - static void do_constructor(lua_State* L, T* obj, call_syntax syntax, int, types) { - default_construct fx{}; - stack::call(types(), types(), L, -1 + static_cast(syntax), fx, obj); - } - - static void match_constructor(lua_State*, T*, call_syntax, int) { - throw error("No matching constructor for the arguments provided"); - } - - template - static void match_constructor(lua_State* L, T* obj, call_syntax syntax, int argcount, types t, Args&&... args) { - if (argcount == sizeof...(CArgs)) { - do_constructor(L, obj, syntax, argcount, t); - return; - } - match_constructor(L, obj, syntax, argcount, std::forward(args)...); - } - static int construct(lua_State* L) { const auto& meta = usertype_traits::metatable; call_syntax syntax = stack::get_call_syntax(L, meta); @@ -186,15 +172,16 @@ namespace sol { T*& referencepointer = *pointerpointer; T* obj = reinterpret_cast(pointerpointer + 1); referencepointer = obj; - match_constructor(L, obj, syntax, argcount - static_cast(syntax), identity_t()...); + int userdataindex = lua_gettop(L); + usertype_detail::match_constructor(L, obj, syntax, argcount - static_cast(syntax), identity_t()...); 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(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::call }); + metafunctiontable.push_back({ name.c_str(), detail::usertype_call }); return; } - functiontable.push_back({ name.c_str(), &base_function::usertype::call }); + indexwrapper.insert({ name, { true, functions.back().get() } }); + functiontable.push_back({ name.c_str(), detail::usertype_call }); } template @@ -304,24 +292,23 @@ namespace sol { int variableend = 0; if (!indexwrapper.empty()) { functions.push_back(std::make_unique("__index", indexfunc, std::move(indexwrapper))); - variablefunctiontable[0] = { "__index", &base_function::usertype::call }; + metafunctiontable.push_back({ "__index", detail::usertype_call }); ++variableend; } if (!newindexwrapper.empty()) { functions.push_back(std::make_unique("__newindex", newindexfunc, std::move(newindexwrapper))); - variablefunctiontable[variableend] = { "__newindex", indexwrapper.empty() ? &base_function::usertype::call : &base_function::usertype::call }; + metafunctiontable.push_back({ "__newindex", indexwrapper.empty() ? detail::usertype_call : detail::usertype_call }); ++variableend; } - variablefunctiontable[variableend] = { nullptr, nullptr }; switch (variableend) { case 2: - destructfunc = &base_function::usertype::gc; + functiongc = detail::usertype_gc; break; case 1: - destructfunc = &base_function::usertype::gc; + functiongc = detail::usertype_gc; break; case 0: - destructfunc = &base_function::usertype::gc; + functiongc = detail::usertype_gc; break; } } @@ -332,10 +319,10 @@ namespace sol { template usertype(constructors, 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)...); @@ -348,20 +335,20 @@ namespace sol { // as all pointered types are considered // to be references. This makes returns of // `std::vector&` and `std::vector*` 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(L, functions, functiontable, metafunctiontable, variablefunctiontable); + usertype_detail::push_metatable(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(L, functions, functiontable, metafunctiontable, variablefunctiontable); + usertype_detail::push_metatable(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(L, destructfunc, functions); + usertype_detail::set_global_deleter(L, functiongc, functions); return 1; } }; diff --git a/tests.cpp b/tests.cpp index 4fca279f..b9c2d8d2 100644 --- a/tests.cpp +++ b/tests.cpp @@ -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" ));