lua - 在 C 中存储闭包,在 C 中调用异步
lua - store closure in C, invoke async in C
我需要一个想法,如何存储 lua 闭包以便稍后异步调用它们。
- 我的第一个想法是
lua_tocfunction
但闭包不是 cfunction 并且不能直接从 C 调用
- 第二个想法是将闭包保存在元表中,这样我就可以推送它并稍后调用它,但似乎我无法复制 闭包。 (
Error: attempt to index a function value
).
所以我需要你的帮助。如何存储闭包?
我承认,我不完全理解为什么我的 lua ctor 中有一个 __index
字段,因为我从某处复制了那部分。
顺便说一句:没有 onrender
的程序按预期运行。我正在使用 qt gui 并且 lua 状态关闭, 在 qt 的主循环之后,因此创建的 window 不会被 __gc
在脚本之后。
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;
}
您的问题似乎是由于使用了错误的索引并试图 set/get 堆栈上错误的 lua 对象上的字段。假设代表您的 GLWindow *
的 udata 首先是 lua 闭包,然后尝试像这样更改代码:
int w_render_onrender(lua_State *L)
{
luaL_checkudata(L, 1, w_render_table);
luaL_checktype(L, 2, LUA_TFUNCTION);
auto *self = chlua_this<GLWindow *>(L, 1, w_render_table);
lua_getmetatable(L, 1);
lua_insert(L, -2); // GLWindow GLWindow_mt lua_closure
lua_setfield(L, -2, "onrender_cb"); // save closure in metatable
self->onrender([L]()
{
luaL_checkudata(L, 1, w_render_table);
// assuming GLWindow udata is self and onrender_cb is your lua closure above
// access GLWindow.onrender_cb through GLWindows's metatable
lua_getfield(L, 1, "onrender_cb");
qDebug() << "onrender";
luaL_checktype(L, -1, LUA_TFUNCTION); // Just to be sure
lua_call(L, 0, 0);
});
return 0;
}
编辑: 在进一步思考之后,使用 luaL_ref
创建一个 lua 引用可能更有意义。这样你就不必关心当 self->onrender
实际运行时堆栈上发生了什么,我假设它是异步的:
int w_render_onrender(lua_State *L)
{
luaL_checkudata(L, 1, w_render_table);
luaL_checktype(L, 2, LUA_TFUNCTION);
auto *self = chlua_this<GLWindow *>(L, 1, w_render_table);
auto lua_cb = luaL_ref(L, LUA_REGISTRYINDEX);
// just to check that what's on the stack shouldn't matter
lua_settop(L, 0);
self->onrender([L, lua_cb]()
{
lua_rawgeti(L, LUA_REGISTRYINDEX, lua_cb);
luaL_checktype(L, -1, LUA_TFUNCTION); // Just to be sure
qDebug() << "onrender";
lua_call(L, 0, 0);
luaL_unref(L, LUA_REGISTRYINDEX, lua_cb); // assuming you're done with it
});
return 0;
}
我需要一个想法,如何存储 lua 闭包以便稍后异步调用它们。
- 我的第一个想法是
lua_tocfunction
但闭包不是 cfunction 并且不能直接从 C 调用 - 第二个想法是将闭包保存在元表中,这样我就可以推送它并稍后调用它,但似乎我无法复制 闭包。 (
Error: attempt to index a function value
).
所以我需要你的帮助。如何存储闭包?
我承认,我不完全理解为什么我的 lua ctor 中有一个 __index
字段,因为我从某处复制了那部分。
顺便说一句:没有 onrender
的程序按预期运行。我正在使用 qt gui 并且 lua 状态关闭, 在 qt 的主循环之后,因此创建的 window 不会被 __gc
在脚本之后。
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;
}
您的问题似乎是由于使用了错误的索引并试图 set/get 堆栈上错误的 lua 对象上的字段。假设代表您的 GLWindow *
的 udata 首先是 lua 闭包,然后尝试像这样更改代码:
int w_render_onrender(lua_State *L)
{
luaL_checkudata(L, 1, w_render_table);
luaL_checktype(L, 2, LUA_TFUNCTION);
auto *self = chlua_this<GLWindow *>(L, 1, w_render_table);
lua_getmetatable(L, 1);
lua_insert(L, -2); // GLWindow GLWindow_mt lua_closure
lua_setfield(L, -2, "onrender_cb"); // save closure in metatable
self->onrender([L]()
{
luaL_checkudata(L, 1, w_render_table);
// assuming GLWindow udata is self and onrender_cb is your lua closure above
// access GLWindow.onrender_cb through GLWindows's metatable
lua_getfield(L, 1, "onrender_cb");
qDebug() << "onrender";
luaL_checktype(L, -1, LUA_TFUNCTION); // Just to be sure
lua_call(L, 0, 0);
});
return 0;
}
编辑: 在进一步思考之后,使用 luaL_ref
创建一个 lua 引用可能更有意义。这样你就不必关心当 self->onrender
实际运行时堆栈上发生了什么,我假设它是异步的:
int w_render_onrender(lua_State *L)
{
luaL_checkudata(L, 1, w_render_table);
luaL_checktype(L, 2, LUA_TFUNCTION);
auto *self = chlua_this<GLWindow *>(L, 1, w_render_table);
auto lua_cb = luaL_ref(L, LUA_REGISTRYINDEX);
// just to check that what's on the stack shouldn't matter
lua_settop(L, 0);
self->onrender([L, lua_cb]()
{
lua_rawgeti(L, LUA_REGISTRYINDEX, lua_cb);
luaL_checktype(L, -1, LUA_TFUNCTION); // Just to be sure
qDebug() << "onrender";
lua_call(L, 0, 0);
luaL_unref(L, LUA_REGISTRYINDEX, lua_cb); // assuming you're done with it
});
return 0;
}