userdata no longer needs to be kept around for its lifetime

can be discarded and lua VM will keep all necessary information
This commit is contained in:
ThePhD 2014-06-27 21:27:48 -07:00
parent 5930818891
commit 36e45d88b2
4 changed files with 90 additions and 34 deletions

View File

@ -184,6 +184,20 @@ struct base_function {
}
};
template<std::size_t N>
struct userdata_gc {
static int gc(lua_State* L) {
for (std::size_t i = 0; i < N; ++i) {
upvalue_t up = stack::get<upvalue_t>(L, i + 1);
base_function* obj = static_cast<base_function*>(up.value);
std::allocator<base_function> 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");
}

View File

@ -50,7 +50,6 @@ enum class lib : char {
class state {
private:
std::vector<std::shared_ptr<void>> classes;
std::unique_ptr<lua_State, void(*)(lua_State*)> L;
table reg;
table global;
@ -154,11 +153,14 @@ public:
template<typename Class, typename... CTor, typename... Args>
state& new_userdata(const std::string& name, Args&&... args) {
constructors<types<CTor...>> ctor;
classes.emplace_back(std::make_shared<userdata<Class>>(name, ctor, std::forward<Args>(args)...));
auto&& ptr = classes.back();
auto udata = std::static_pointer_cast<userdata<Class>>(ptr);
global.set_userdata(*udata);
constructors<types<CTor...>> ctor{};
return new_userdata(name, ctor, std::forward<Args>(args)...);
}
template<typename Class, typename... CArgs, typename... Args>
state& new_userdata(const std::string& name, constructors<CArgs...> ctor, Args&&... args) {
userdata<Class> udata(name, ctor, std::forward<Args>(args)...);
global.set_userdata(udata);
return *this;
}

View File

@ -52,6 +52,7 @@ private:
std::vector<luaL_Reg> ptrfunctiontable;
std::vector<luaL_Reg> metafunctiontable;
std::vector<luaL_Reg> ptrmetafunctiontable;
lua_CFunction cleanup;
template<typename... TTypes>
struct constructor {
@ -97,7 +98,6 @@ private:
}
};
template<std::size_t N>
struct destructor {
static int destruct(lua_State* L) {
userdata_t udata = stack::get<userdata_t>(L, 1);
@ -144,20 +144,24 @@ public:
template<typename... Args, typename... CArgs>
userdata(std::string name, constructors<CArgs...>, 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>(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<sizeof...(Args)>::gc;
build_function_tables<0, 0>(std::forward<Args>(args)...);
functionnames.push_back("new");
functiontable.push_back({ functionnames.back().c_str(), &constructor<CArgs...>::construct });
functionnames.push_back("__gc");
metafunctiontable.push_back({ functionnames.back().c_str(), &destructor<sizeof...(Args) / 2>::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<CArgs...> c, Args&&... args) :
userdata(std::string(name), std::move(c), std::forward<Args>(args)...) {}
const std::vector<std::string>& function_names () const {
std::vector<std::string>& function_names () {
return functionnames;
}
const std::vector<std::unique_ptr<base_function>>& functions () const {
std::vector<std::unique_ptr<base_function>>& functions () {
return funcs;
}
const std::vector<std::unique_ptr<base_function>>& reference_functions () const {
std::vector<std::unique_ptr<base_function>>& reference_functions () {
return ptrfuncs;
}
const std::vector<std::unique_ptr<base_function>>& meta_functions () const {
std::vector<std::unique_ptr<base_function>>& meta_functions () {
return metafuncs;
}
const std::vector<std::unique_ptr<base_function>>& meta_reference_functions () const {
std::vector<std::unique_ptr<base_function>>& meta_reference_functions () {
return ptrmetafuncs;
}
const std::vector<luaL_Reg>& function_table () const {
std::vector<luaL_Reg>& function_table () {
return functiontable;
}
const std::vector<luaL_Reg>& reference_function_table () const {
std::vector<luaL_Reg>& reference_function_table () {
return ptrfunctiontable;
}
const std::vector<luaL_Reg>& meta_function_table () const {
std::vector<luaL_Reg>& meta_function_table () {
return metafunctiontable;
}
const std::vector<luaL_Reg>& meta_reference_function_table () const {
std::vector<luaL_Reg>& 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<std::string, 19> userdata<T>::metafunctionnames = {
namespace stack {
template <typename T>
struct pusher<userdata<T>> {
template <bool release = false, typename TCont>
static int push_upvalues (lua_State* L, TCont&& cont) {
int n = 0;
for (auto& c : cont) {
if (release)
stack::push<upvalue_t>(L, c.release());
else
stack::push<upvalue_t>(L, c.get());
++n;
}
return n;
}
template <typename Meta, typename Funcs, typename FuncTable, typename MetaFuncs, typename MetaFuncTable>
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<upvalue_t>(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<uint32_t>(funcs.size()));
// meta functions
for (std::size_t u = 0; u < metafuncs.size(); ++u) {
stack::push<upvalue_t>(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<uint32_t>(metafuncs.size()));
lua_pushvalue(L, -1);
lua_setfield(L, -1, "__index");
}
@ -262,6 +283,21 @@ struct pusher<userdata<T>> {
// 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<T*>::metatable, user.reference_functions(), user.reference_function_table(), user.meta_reference_functions(), user.meta_reference_function_table());
push_metatable(L, userdata_traits<T>::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<true>(L, user.functions());
up += push_upvalues<true>(L, user.reference_functions());
up += push_upvalues<true>(L, user.meta_functions());
up += push_upvalues<true>(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<T>::gctable[0]));
}
};
} // stack

View File

@ -30,6 +30,7 @@ template<typename T>
struct userdata_traits {
static const std::string name;
static const std::string metatable;
static const std::string gctable;
};
template<typename T>
@ -38,6 +39,9 @@ const std::string userdata_traits<T>::name = detail::demangle(typeid(T));
template<typename T>
const std::string userdata_traits<T>::metatable = std::string("sol.").append(detail::demangle(typeid(T)));
template<typename T>
const std::string userdata_traits<T>::metatable = std::string("sol.").append(detail::demangle(typeid(T))).append(".\xE2\x99\xBB");
}
#endif // SOL_USERDATA_TRAITS_HPP