【问题标题】:lua - store closure in C, invoke async in Clua - 在 C 中存储闭包,在 C 中调用异步
【发布时间】:2017-05-15 13:21:30
【问题描述】:

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

  1. 我的第一个想法是lua_tocfunction,但闭包不是 cfunction,不能直接从 C 调用
  2. 第二个想法是将闭包保存在元表中,我可以推送它并稍后调用它,但似乎我无法复制一个闭包。 (Error: attempt to index a function value)。

所以我需要你的帮助。如何存储闭包?

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

顺便说一句:没有onrender 的程序按预期工作。我正在使用 qt gui 并且 lua 状态已关闭, 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;
}

【问题讨论】:

  • 您确定w_render_onrender 中的索引正确吗?我假设代表您的 GLWindow * 的 self udata 首先是,其次是闭包。在哪种情况下不应该是lua_setfield(L, -3, "onrender_cb");lua_setfield(L, 1, "onrender_cb");
  • 我认为您需要做的是lua_getmetatable(L, 1); lua_insert(L, -2);,然后才能真正尝试设置该字段。我不确定这里是否真的需要 pushvalue。无论如何,这不会在您的 onrender lambda 中使用,因为您正在执行 lua_getfield 来取回闭包。
  • @greatwolf 我已经尝试了你的 2 个解决方案:-3 或 1 给我attempt to index a userdata value,第二个解决方案也给我attempt to index a userdata value。我注意到的一件事是,应该使用 1 调用 get/setfield,因为 doc 说,索引是表存储在堆栈中的位置,现在我想知道 lambda 的实现如何可以肯定的是,桌子在那里,同时没有人改变堆栈。
  • 你是说lua_getmetatable(L, 1);attempt to index a userdata value?这真的没有意义。那是元表集正确的GLWindow * udata?
  • @greatwolf 不管我的 impl 出了什么问题,你的 answer 有效;)

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


【解决方案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-&gt;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;
}

【讨论】:

  • 第二个代码 sn-p 就像一个魅力非常感谢你!我是对的,万恶之源是异步 C 代码无法访问元表,因为未知堆栈L?一次引用 udata 本身(在 lua ctor 中)以便能够在lua_CFunction 中同步获取它并通过捕获第二个解决方案中的引用将其传递给 lambda,难道不是更好的设计吗?这样,我每个对象只有一个 ref,因为我假设每个对象会有很多回调。不过,我会将您的答案标记为已接受:)。
  • @Aitch 是的,您可以将 lua_cb 分配给 udata,然后在之后引用 udata(只需提供 __newindex 否则您将再次收到该 udata 索引错误)。为了简单起见,我直接引用了回调。
  • @Aitch 如果self-&gt;onrender 真的是异步的,那么在回调本身实际发生之前,可以在 lua vm 上执行任意数量的事情。在这种情况下,当时实际在堆栈上的内容可能与调用 w_render_onrender 时的内容大不相同。我对异步调用的工作感到有点惊讶,lua 的设计没有内部锁或同步原语。你如何确保L 是一致的,并且在运行字节码指令的过程中没有被抢占?
  • 我试图编写一个基于闭包的对象来拥有一个异步示例 here 但 lua 说 bootstrap.lua:12: attempt to call upvalue 'onrender_cb' (a table value) :( 很遗憾。实际上这几乎是我期望我的 C 代码执行异步的. 如果我 1. 不使用 yield 和 2. L 上的操作永远不会是多线程的,它应该可以工作,不是吗?如果没有 yield 和协程,抢占是不可能的,对吧?我已经搜索了脚本可能性和 lua impl 很简单,但我不想放弃异步脚本:(
  • 或者我可能需要使用lua_State* cL = lua_newthread(L); 之类的东西来像协程一样进行异步调用?这应该是可能的原因是,我发现 (LuaNode)[lua.org/wshop13/Burgueno.pdf] 执行异步 io。也许我看看他们对异步调用的 impl。
猜你喜欢
  • 1970-01-01
  • 2020-01-15
  • 1970-01-01
  • 2011-03-18
  • 1970-01-01
  • 2021-04-02
  • 2015-12-14
  • 1970-01-01
  • 2012-10-01
相关资源
最近更新 更多