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