I need an idea, how I can store lua closures to invoke them asynchronously later.
- my first idea was
lua_tocfunction
but a closure is not a cfunction and cannot be invoked from C directly - second idea was to save the closure in the metatable, that I can push it and call it later, but it seems, that I cannot copy a closure. (
Error: attempt to index a function value
).
So I need your help please. How can I store a closure?
I admit, that I did not completely understand why there is an __index
field in my lua ctor as I've copied that part from somewhere.
By the way: the program without onrender
worked as expected. I'm using qt gui and the lua-states are closed, after qt's main loop, thus the created window is not going to be delete by __gc
after the script.
bootstrap.lua
local w = w_render() -- create window object
w:show()
w:onrender(function()
print('render')
end)
w_lua.cpp
// chlua_* are helper macros/templates/methods
// 1: self
// 2: render closure
int w_render_onrender(lua_State *L) {
auto *self = chlua_this<GLWindow *>(L, 1, w_render_table);
lua_pushvalue(L, 2); // copy closure to top
lua_setfield(L, 2, "onrender_cb"); // save closure in metatable
// !!! ERROR: attempt to index a function value
self->onrender([L](){
lua_getfield(L, 2, "onrender_cb");
qDebug() << "onrender";
lua_call(L, 0, 0);
});
return 0;
}
// Creates the object
int w_render(lua_State *L) {
auto *&self = chlua_newuserdata<GLWindow *>(L);
self = new GLWindow;
if (luaL_newmetatable(L, w_render_table)) {
luaL_setfuncs(L, w_render_methods, 0);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
}
lua_setmetatable(L, -2);
return 1;
}
It looks like your problem is stemming from using the wrong indices and attempting to set/get fields on the wrong lua object on the stack. Assuming the udata representing your
GLWindow *
is first followed by the lua closure second, try changing the code like this:Edit: After thinking about this some more, it probably makes more sense to create a lua reference using
luaL_ref
. This way you don't have to care what happens to be on the stack whenself->onrender
actually runs, which I'm assuming is async: