在 Lua 和 C++ 之间传递引用参数

我已经注册了一个 Lua 函数,该函数期望作为参数传递一个元表,在 C++ 运行时我想修改表的内容:

int operate(lua_State *L)
{
    std::vector<int> values{};

    if (auto length = get_length_at_index(L, 1))
    {
        result.reserve(length);

        lua_pushnil(L);
        while (lua_next(L, 1))
        {
            result.push_back(lua_tointeger(L, -1));
            lua_pop(L, 1);
        }
    }

    for (auto &v : values)
    {
        v *= 2;
    }

    return 0;
}

int main()
{
    auto L = luaL_newstate();

    luaL_dofile(L, "script.lua");
    lua_register(L, "operate", operate);

    return 0;
}

上面的代码读取了 "lua.script",看起来像这样:

values = {1, 2, 3, 4, 5, 6}
operate(values)

for index = 1, #values do
    print(values[index])
end

期望输出是从1到6的值乘以2,但输出是未修改的值。很明显,这是因为我正在将元表 复制std::vector 并修改该副本。

有没有一种从 C++ 运行时操作 Lua 对象的方法,以便它们反映从 C++ 应用的修改?


PS:我知道可以将表作为函数返回值返回,但我想知道是否有其他选项。

点赞
用户3446285
用户3446285

第一个循环后,表格仍然在堆栈的顶部,简化了以下解决方案。将第二个循环替换为:

int count = int(result.size());
for (int i = 0; i < count; ++i) {
    lua_pushinteger(L, result[i] * 2);
    lua_rawseti(L, -2, i);
}

-2 指的是堆栈顶部下面的 lua 值(类似地,-1 指的是顶部)。当到达 rawseti 时,您的 lua 堆栈看起来类似于:

[-1]:result[i] 的副本

[-2]:您的 values 表格

rawseti 将堆栈顶部的值弹出并存储在指定的表格和索引中,将表格再次放在堆栈顶部。

请注意,实际上您可以在第一个循环中执行此操作,但是应避免养成这种习惯,因为某些 lua 操作在迭代时会导致未定义的行为,并且会进一步复杂化代码。参见 此帖子 以获得澄清。

为什么会发生这种情况?

总之,代码写入 C++ 内存,但不写入 lua 表格。

第一个循环迭代,读取每个值并将其添加到 result 容器中。

lua_pushnil(L);
while (lua_next(L, 1))
{
    result.push_back(lua_tointeger(L, -1));
    lua_pop(L, 1);
}

第二个循环在 C++ 容器内使每个元素的值加倍。

for (auto &v : values)
{
    v *= 2;
}
2017-08-20 23:11:57
用户1944004
用户1944004

你不能在 Lua 栈上获取一个值的引用。但是你可以直接修改表。

#include <cassert>
#include <lua.hpp>

int operate(lua_State *L)
{
  assert(lua_istable(L,1));

  int const N = lua_rawlen(L,1);

  for (int i = 1; i <= N; ++i)
  {
    lua_pushinteger(L, i);
    lua_gettable(L,1);
    int value = lua_tonumber(L,-1);

    value *= 2;

    lua_pushinteger(L, i);
    lua_pushinteger(L, value);
    lua_settable(L,1);
  }

  return 0;
}

int main()
{
    auto L = luaL_newstate();
    luaL_openlibs(L);

    lua_register(L, "operate", operate);
    luaL_dofile(L, "script.lua");
}
2017-08-20 23:16:52
用户3125367
用户3125367

由于传递给函数的参数会被复制到被调用者的栈中,因此只有传递的表格(和用户数据)可以被改变。既然在你的函数中这种情况存在,可以创建一个表格封装类,该类可以重载[]来调用lua_gettable()/lua_settable(),但这可能不值得而且更像是C++问题。

另请参见https://github.com/ThePhD/sol2/more也是如此。与大多数C++模板<T&&>代理库一样,很难快速判断sol2是否能够执行't[i] *= 2',所以您可能会有不同的体验。

2017-08-21 04:50:17