lua - 在C中存储闭包,在C中调用async

时间:2016-12-31 15:56:23

标签: c++ c lua lua-api lua-c++-connection

我需要一个想法,我如何能够存储lua闭包以便以后异步调用它们。

  1. 我的第一个想法是lua_tocfunction,但是闭包不是一个函数,不能直接从C调用
  2. 第二个想法是将闭包保存在metatable中,我可以推送它并稍后调用它,但似乎我不能复制一个闭包。 (Error: attempt to index a function value)。
  3. 所以我需要你的帮助。我怎样才能存储封闭物?

    我承认,我并不完全理解为什么我的lua ctor中存在__index字段,因为我已经从某个地方复制了该部分。

    顺便说一句:没有onrender的程序按预期工作。我使用qt gui并关闭了lua-states, qt的主循环之后,因此创建的窗口不会被__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;
    }
    

1 个答案:

答案 0 :(得分:1)

看起来您的问题源于使用错误的索引并尝试在堆栈上的错误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;
}