From 36e45d88b29e5c93f2d2586bbc83c6d0655ed7fb Mon Sep 17 00:00:00 2001 From: ThePhD Date: Fri, 27 Jun 2014 21:27:48 -0700 Subject: [PATCH] userdata no longer needs to be kept around for its lifetime can be discarded and lua VM will keep all necessary information --- sol/function_types.hpp | 14 +++++++ sol/state.hpp | 14 ++++--- sol/userdata.hpp | 92 ++++++++++++++++++++++++++++------------- sol/userdata_traits.hpp | 4 ++ 4 files changed, 90 insertions(+), 34 deletions(-) diff --git a/sol/function_types.hpp b/sol/function_types.hpp index fe255fe8..fb6b79b7 100644 --- a/sol/function_types.hpp +++ b/sol/function_types.hpp @@ -184,6 +184,20 @@ struct base_function { } }; + template + struct userdata_gc { + static int gc(lua_State* L) { + for (std::size_t i = 0; i < N; ++i) { + upvalue_t up = stack::get(L, i + 1); + base_function* obj = static_cast(up.value); + std::allocator alloc{}; + alloc.destroy(obj); + alloc.deallocate(obj, 1); + } + return 0; + } + }; + virtual int operator()(lua_State*) { throw error("failure to call specialized wrapped C++ function from Lua"); } diff --git a/sol/state.hpp b/sol/state.hpp index 5cf5630f..61464f97 100644 --- a/sol/state.hpp +++ b/sol/state.hpp @@ -50,7 +50,6 @@ enum class lib : char { class state { private: - std::vector> classes; std::unique_ptr L; table reg; table global; @@ -154,11 +153,14 @@ public: template state& new_userdata(const std::string& name, Args&&... args) { - constructors> ctor; - classes.emplace_back(std::make_shared>(name, ctor, std::forward(args)...)); - auto&& ptr = classes.back(); - auto udata = std::static_pointer_cast>(ptr); - global.set_userdata(*udata); + constructors> ctor{}; + return new_userdata(name, ctor, std::forward(args)...); + } + + template + state& new_userdata(const std::string& name, constructors ctor, Args&&... args) { + userdata udata(name, ctor, std::forward(args)...); + global.set_userdata(udata); return *this; } diff --git a/sol/userdata.hpp b/sol/userdata.hpp index 6c317a5e..42cd9b7b 100644 --- a/sol/userdata.hpp +++ b/sol/userdata.hpp @@ -52,6 +52,7 @@ private: std::vector ptrfunctiontable; std::vector metafunctiontable; std::vector ptrmetafunctiontable; + lua_CFunction cleanup; template struct constructor { @@ -97,7 +98,6 @@ private: } }; - template struct destructor { static int destruct(lua_State* L) { userdata_t udata = stack::get(L, 1); @@ -144,20 +144,24 @@ public: template userdata(std::string name, constructors, Args&&... args): luaname(std::move(name)) { functionnames.reserve(sizeof...(args) + 2); - functiontable.reserve(sizeof...(args) + 2); - ptrfunctiontable.reserve(sizeof...(args) + 2); - metafunctiontable.reserve(sizeof...(args) + 2); - ptrmetafunctiontable.reserve(sizeof...(args) + 2); - funcs.reserve(sizeof...(args) + 2); - ptrfuncs.reserve(sizeof...(args) + 2); - metafuncs.reserve(sizeof...(args) + 2); - ptrmetafuncs.reserve(sizeof...(args) + 2); - build_function_tables<0, 0>(std::forward(args)...); + functiontable.reserve(sizeof...(args)); + ptrfunctiontable.reserve(sizeof...(args)); + metafunctiontable.reserve(sizeof...(args)); + ptrmetafunctiontable.reserve(sizeof...(args)); + + funcs.reserve(sizeof...(args)); + ptrfuncs.reserve(sizeof...(args)); + metafuncs.reserve(sizeof...(args)); + ptrmetafuncs.reserve(sizeof...(args)); + + cleanup = &base_function::userdata_gc::gc; + + build_function_tables<0, 0>(std::forward(args)...); functionnames.push_back("new"); functiontable.push_back({ functionnames.back().c_str(), &constructor::construct }); functionnames.push_back("__gc"); - metafunctiontable.push_back({ functionnames.back().c_str(), &destructor::destruct }); + metafunctiontable.push_back({ functionnames.back().c_str(), &destructor::destruct }); // ptr_functions does not participate in garbage collection/new, // as all pointered types are considered // to be references. This makes returns of @@ -173,42 +177,46 @@ public: userdata(const char* name, constructors c, Args&&... args) : userdata(std::string(name), std::move(c), std::forward(args)...) {} - const std::vector& function_names () const { + std::vector& function_names () { return functionnames; } - const std::vector>& functions () const { + std::vector>& functions () { return funcs; } - const std::vector>& reference_functions () const { + std::vector>& reference_functions () { return ptrfuncs; } - const std::vector>& meta_functions () const { + std::vector>& meta_functions () { return metafuncs; } - const std::vector>& meta_reference_functions () const { + std::vector>& meta_reference_functions () { return ptrmetafuncs; } - const std::vector& function_table () const { + std::vector& function_table () { return functiontable; } - const std::vector& reference_function_table () const { + std::vector& reference_function_table () { return ptrfunctiontable; } - const std::vector& meta_function_table () const { + std::vector& meta_function_table () { return metafunctiontable; } - const std::vector& meta_reference_function_table () const { + std::vector& meta_reference_function_table () { return ptrmetafunctiontable; } + lua_CFunction cleanup_function () { + return cleanup; + } + const std::string& name () const { return luaname; } @@ -240,19 +248,32 @@ 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])); - // regular functions accessed through __index semantics - for (std::size_t u = 0; u < funcs.size(); ++u) { - stack::push(L, funcs[u].get()); + if (functable.size() > 1) { + // regular functions accessed through __index semantics + int up = push_upvalues(L, funcs); + luaL_setfuncs(L, functable.data(), up); } - luaL_setfuncs(L, functable.data(), static_cast(funcs.size())); - // meta functions - for (std::size_t u = 0; u < metafuncs.size(); ++u) { - stack::push(L, metafuncs[u].get()); + if (metafunctable.size() > 1) { + // meta functions + int up = push_upvalues(L, metafuncs); + luaL_setfuncs(L, metafunctable.data(), up); } - luaL_setfuncs(L, metafunctable.data(), static_cast(metafuncs.size())); lua_pushvalue(L, -1); lua_setfield(L, -1, "__index"); } @@ -262,6 +283,21 @@ struct pusher> { // 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])); } }; } // stack diff --git a/sol/userdata_traits.hpp b/sol/userdata_traits.hpp index 27e044f8..aad22b12 100644 --- a/sol/userdata_traits.hpp +++ b/sol/userdata_traits.hpp @@ -30,6 +30,7 @@ template struct userdata_traits { static const std::string name; static const std::string metatable; + static const std::string gctable; }; template @@ -38,6 +39,9 @@ const std::string userdata_traits::name = detail::demangle(typeid(T)); template const std::string userdata_traits::metatable = std::string("sol.").append(detail::demangle(typeid(T))); +template +const std::string userdata_traits::metatable = std::string("sol.").append(detail::demangle(typeid(T))).append(".\xE2\x99\xBB"); + } #endif // SOL_USERDATA_TRAITS_HPP