传递参数作为从Lua到C ++的参考

时间:2017-08-20 22:23:27

标签: c++ lua

我注册了一个Lua函数,它需要一个metatable作为参数,我想在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,但输出是未修改的值。很明显,这是因为我将metatable复制std::vector并修改副本。

有没有办法通过Lua对象从C ++运行时运行它们反映从C ++应用的更改?

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

3 个答案:

答案 0 :(得分:2)

编辑:我没有看到您已经知道原因。对于未来的读者,这个解释已经移到了底层。

在第一个循环之后,您的表保持在堆栈顶部,简化了以下解决方案。用以下代码替换你的第二个循环:

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操作在迭代时会导致未定义的行为,并且它会进一步使代码复杂化。有关说明,请参阅this post

为什么会这样?

总之,代码写入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;
}

答案 1 :(得分:1)

当传递给函数的参数被复制到被调用堆栈时,只能传递表(和userdata)。正如您的函数中的情况一样,可以创建一个表包装类,它可以将[]重载到lua_gettable()/ lua_settable(),但它可能不值得,更像是C ++问题。

同样请参阅https://github.com/ThePhD/sol2/more。与大多数C ++模板一样&lt; T&amp;&amp;&gt;代理库,很难快速判断sol2是否可以t[i] *= 2,所以ymmw。

答案 2 :(得分:0)

您无法获得对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");
}