diff --git a/src/luamm.cc b/src/luamm.cc index 961cd126..abf28cc4 100644 --- a/src/luamm.cc +++ b/src/luamm.cc @@ -31,16 +31,6 @@ namespace lua { const char lua_exception_namespace[] = "lua::lua_exception_namespace"; const char this_cpp_object [] = "lua::this_cpp_object"; - // destructor for C++ objects stored as lua userdata - template - int destroy_cpp_object(lua_State *l) - { - T *ptr = static_cast(lua_touserdata(l, -1)); - assert(ptr); - ptr->~T(); - return 0; - } - // converts C++ exceptions to strings, so lua can do something with them int exception_to_string(lua_State *l) { @@ -247,13 +237,15 @@ namespace lua { rawsetfield(-2, "__tostring"); pushboolean(false); rawsetfield(-2, "__metatable"); - lua_pushcfunction(cobj.get(), &destroy_cpp_object); + pushdestructor(); rawsetfield(-2, "__gc"); pop(); // a metatable for C++ functions callable from lua code newmetatable(cpp_function_metatable); - lua_pushcfunction(cobj.get(), &destroy_cpp_object); + pushboolean(false); + rawsetfield(-2, "__metatable"); + pushdestructor(); rawsetfield(-2, "__gc"); pop(); diff --git a/src/luamm.hh b/src/luamm.hh index 49c0066c..052edc32 100644 --- a/src/luamm.hh +++ b/src/luamm.hh @@ -143,6 +143,22 @@ namespace lua { class state { std::shared_ptr cobj; + // destructor for C++ objects stored as lua userdata + template + static int destroy_cpp_object(lua_State *l) + { + T *ptr = static_cast(lua_touserdata(l, -1)); + assert(ptr); + try { + // throwing exceptions in destructors is a bad idea + // but we catch (and ignore) them, just in case + ptr->~T(); + } + catch(...) { + } + return 0; + } + bool safe_compare(lua_CFunction trampoline, int index1, int index2); public: state(); @@ -218,6 +234,13 @@ namespace lua { int ref(int t) { return luaL_ref(cobj.get(), t); } // len recieves length, if not null. Returned value may contain '\0' const char* tocstring(int index, size_t *len = NULL) { return lua_tolstring(cobj.get(), index, len); } + // Don't use pushclosure() to create a __gc function. The problem is that lua calls them + // in an unspecified order, and we may end up destroying the object holding the + // std::function before we get a chance to call it. This pushes a function that simply + // calls ~T when the time comes. Only set it as __gc on userdata of type T. + template + void pushdestructor() + { lua_pushcfunction(cobj.get(), &destroy_cpp_object); } // type c, throw everything but the kitchen sink // call() is a protected mode call, we don't allow unprotected calls