【问题标题】:Getting reference to userdata in a table using luaL_ref?使用 luaL_ref 获取对表中用户数据的引用?
【发布时间】:2019-02-20 14:55:20
【问题描述】:

首先,我很抱歉没有上传完整的代码。

我正在尝试将userdata 转换为pointer,以便可以使用lua_rawgeti() 将其传递给其他lua 块。

如果你看到 outletRet() 函数,它会检查返回值的类型,如果是 userdata,它会通过调用 outlet_pointer() 将指针传递给其他 lua 块,它似乎工作正常。

如果userdatatable 中,则调用outletTable() 函数。如果其中一个元素是userdata,它还会将userdata 转换为pointer,然后通过调用outlet_pointer() 将其传递给其他块。

但是,当调用 luaL_ref(L, LUA_REGISTRYINDEX) 时,它似乎获得了对整个 table 的引用,而不仅仅是其中的 userdata

如何获得对userdata 的引用而不是整个table

void ofLua::outletTable() //called from outletRet() below
{
    lua_pushvalue(L, -1);
    lua_pushnil(L);
    int ac = 0;
    t_atom *av = static_cast<t_atom *>(getbytes(sizeof(t_atom) * ac));
    while (lua_next(L, -2))
    {
        av = static_cast<t_atom *>(resizebytes(av, sizeof(t_atom) * ac,
                                               sizeof(t_atom) * (ac + 1)));
        if (lua_isboolean(L, -1))
        {
            av[ac].a_type = A_FLOAT;
            av[ac].a_w.w_float = static_cast<t_float>(lua_toboolean(L, -1));
        }
        else if (lua_isnumber(L, -1))
        {
            av[ac].a_type = A_FLOAT;
            av[ac].a_w.w_float = static_cast<t_float>(lua_tonumber(L, -1));
        }
        else if (lua_isstring(L, -1))
        {
            av[ac].a_type = A_SYMBOL;
            av[ac].a_w.w_symbol = gensym(lua_tostring(L, -1));
        }
        else if (lua_isuserdata(L, -1))
        {
            av[ac].a_type = A_POINTER;
        }
        ac++;
        lua_pop(L, 1);
    }
    lua_pop(L, 1);
    const ofeliaIO &io = dataPtr->io;
    if (io.hasMultiControlOutlets)
    {
        int last = (io.numOutlets >= ac ? ac : io.numOutlets) - 1;
        for (int i = last; i >= 0; --i)
        {
            if (av[i].a_type == A_FLOAT)
                outlet_float(io.outlets[i], av[i].a_w.w_float);
            else if (av[i].a_type == A_SYMBOL)
                outlet_symbol(io.outlets[i], av[i].a_w.w_symbol);
            else if (av[i].a_type == A_POINTER)
            {
                userDataRefVec.push_back(luaL_ref(L, LUA_REGISTRYINDEX));
                outlet_pointer(io.outlets[i], reinterpret_cast<t_gpointer *>(&userDataRefVec.back()));
                luaL_unref(L, LUA_REGISTRYINDEX, userDataRefVec.back());
                userDataRefVec.pop_back();
            }
        }
    }
    else
        outlet_list(dataPtr->ob.ob_outlet, &s_list, ac, av);
    freebytes(av, sizeof(t_atom) * ac);
}

void ofLua::outletRet()
{
    const ofeliaIO &io = dataPtr->io;
    if (!io.hasControlOutlet) return;
    if (lua_isnil(L, -1))
        outlet_bang(io.outlets[0]);
    else if (lua_isboolean(L, -1))
        outlet_float(io.outlets[0], static_cast<t_float>(lua_toboolean(L, -1)));
    else if (lua_isnumber(L, -1))
        outlet_float(io.outlets[0], static_cast<t_float>(lua_tonumber(L, -1)));
    else if (lua_isstring(L, -1))
        outlet_symbol(io.outlets[0], gensym(lua_tostring(L, -1)));
    else if (lua_isuserdata(L, -1))
    {
        userDataRefVec.push_back(luaL_ref(L, LUA_REGISTRYINDEX));
        outlet_pointer(io.outlets[0], reinterpret_cast<t_gpointer *>(&userDataRefVec.back()));
        luaL_unref(L, LUA_REGISTRYINDEX, userDataRefVec.back());
        userDataRefVec.pop_back();
    }
    else if (lua_istable(L, -1))
        outletTable();
}

【问题讨论】:

  • outlet_pointer(io.outlets[0], reinterpret_cast&lt;t_gpointer *&gt;(&amp;userDataRefVec.back()));这是什么?
  • @IllidanS4 将userDataRefVec 的最后一个元素整数的地址传递给其他Lua 块,因此可以通过lua_rawgeti() 在那里使用userData。很抱歉,我无法解释更多细节。
  • 有什么理由将它作为指针传递?为什么不简单地传递整数?
  • @IllidanS4 因为我想区分numberuserdata,所以它们不会冲突。

标签: c++ lua lua-userdata


【解决方案1】:

我正在尝试将用户数据转换为指针

不要。不一样,指针不代表用户数据。 您不能通过指向其数据区域的指针来检索 Lua 用户数据对象。

如果您需要在本机端使用用户数据进行操作,请将用户数据以luaL_ref() 保存在注册表中,并使用返回的整数作为参考。稍后您可以从注册表中检索该对象,并在需要时获取指向其数据区域的指针。但不要只存储指针。

关于引用表的问题,以luaL_ref()保存在注册表中的对象必须在Lua栈顶。但现在唯一留在 Lua 栈顶的是桌子。您复制该表的值以使用 while(lua_next()) 进行迭代,并且在该循环内从该表中读取的任何内容都会在循环结束时从堆栈中弹出。

您可能根本不应该构建av 结构,而是在扫描原始表时立即处理数据。你不需要引用/取消引用用户数据。 如果你真的必须拥有av,那么不要使用指向用户数据内存的指针,而是保存luaL_ref() 返回的整数索引。你应该在第一个循环中进行,而不是在第二个循环中。然后第二个循环将执行lua_rawgeti() 以获取 userdata 对象,处理后您可以取消引用它。

【讨论】:

  • 感谢您的回答,但是当我在第一个循环(即 while (lua_next(L, -2)))中调用 luaL_ref(L, LUA_REGISTRYINDEX); 时,我的应用程序崩溃了。
  • lua_pop(L,1); 移动到除else if (lua_isuserdata(L,-1)) 之外的每个条件中的while 循环中使其不会崩溃。也许我不应该在调用LuaL_ref()之后再调用lua_pop(L,1)
  • @ZackLee,使用luaL_ref() 保存某些内容会将其从堆栈中弹出,因此请使用lua_pushvalue() 进行复制。但首先检查一下 - 如果您扫描的表将在整个处理过程中存在,您不需要将用户数据保存在注册表中。您可以将指针指向av 的用户数据内存区域,并依赖于用户数据将由存储它的表保持活动状态这一事实。那么就不需要引用/取消引用它了。
  • 非常感谢您的帮助。明天我会给你一个赏金。
猜你喜欢
  • 2013-10-20
  • 2020-08-24
  • 2014-06-26
  • 2014-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多