【问题标题】:Pass parameters as reference from Lua to C++将参数作为引用从 Lua 传递到 C++
【发布时间】:2017-08-21 12:50:17
【问题描述】:

我注册了一个 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:我知道我可以将表作为函数返回值返回,但我想知道是否还有其他选择。

【问题讨论】:

  • 在任何情况下,在加载使用它的脚本之前,您都必须在解释器中注册该函数。同样没有luaL_openlibs(L),您将无法使用print

标签: c++ lua


【解决方案1】:

编辑:我没有看到您已经知道原因。解释已移至底部以供未来读者阅读。

在第一个循环之后,您的表仍位于堆栈顶部,从而简化了以下解决方案。将第二个循环替换为:

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;
}

【讨论】:

    【解决方案2】:

    当传递给函数的参数被复制到被调用者堆栈时,只有传递的表(和用户数据)可以被改变。由于您的函数就是这种情况,因此可以创建一个表包装类,它可以将 [] 重载到 lua_gettable()/lua_settable(),但这可能不值得,更像是 C++ 问题。

    另见https://github.com/ThePhD/sol2/more 之类的。与大多数 C++ 模板 代理库一样,很难快速判断 sol2 是否可以做到 t[i] *= 2,所以 ymmw。

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        猜你喜欢
        • 2014-05-14
        • 1970-01-01
        • 2013-07-24
        • 1970-01-01
        • 2021-03-30
        • 2016-11-06
        • 2013-07-13
        • 2012-05-27
        • 2018-12-15
        相关资源
        最近更新 更多