lua - 在 C 中存储闭包并在 C 中异步调用

我需要一个思路,如何存储 lua 闭包以便稍后异步调用它们。

  1. 我的第一个想法是使用 lua_tocfunction,但闭包不是 C 函数,无法直接从 C 中调用它
  2. 第二个想法是将闭包保存在元表中,这样我就可以推送它并在稍后调用它,但似乎我无法“复制”一个闭包。(错误:尝试索引函数值)。

所以请帮帮我。我该如何存储闭包?

我承认我并没有完全理解为什么在我的 lua 构造器中有一个__index字段,因为我从某个地方抄了这一部分。

顺便说一下:没有 onrender 的程序按预期运行。我正在使用 qt gui,并且在 qt 的主循环之后关闭了 lua 状态,因此脚本执行后,创建的窗口不会被 __gc 删除。

bootstrap.lua

local w = w_render() -- 创建窗口对象
w:show()

w:onrender(function()
    print('render')
end)

w_lua.cpp

// chlua_* 是帮助宏/模板/方法
// 1:self
// 2:渲染闭包

int w_render_onrender(lua_State *L) {
    auto *self = chlua_this<GLWindow *>(L, 1, w_render_table);

    lua_pushvalue(L, 2); // 将闭包复制到顶部
    lua_setfield(L, 2, "onrender_cb"); // 将闭包保存在元表中
    // !!! 错误:尝试索引函数值

    self->onrender([L](){
        lua_getfield(L, 2, "onrender_cb");
        qDebug() << "onrender";
        lua_call(L, 0, 0);
    });

    return 0;
}

// 创建对象
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;
}
点赞
用户234175
用户234175

似乎你的问题来自于使用错误的索引在 lua 对象栈上尝试设置/获取字段。假设表示你的 GLWindow * 的 udata 是第一个,然后跟着是第二个 lua closure,尝试像这样更改代码:

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;
}
2016-12-31 18:08:45