调用 `lua_pcall` 会清除 `lua_State`。

huangapple go评论81阅读模式
英文:

Calling lua_pcall clear the lua_State

问题

以下是您要求的代码翻译部分:

这是我正在测试的代码的简化版本:

int f(lua_State *L)
{
    std::cout << std::format("- Function call, stack size: {}, parameter value: {}\n", lua_gettop(L), lua_tointeger(L, 1));
    return 0;
}

int main()
{
    lua_State *const L = luaL_newstate();
    luaL_loadstring(L, "f1(1)");
    luaL_loadstring(L, "f2(2)");
    lua_register(L, "f1", f);
    lua_register(L, "f2", f);

    std::cout << "\nStack before call:\n";
    for (int b = 0, e = lua_gettop(L); b != e; ++b)
        std::cout << '\t' << lua_typename(L, lua_type(L, b + 1)) << '\n';

    for (int i = 0; lua_pcall(L, 0, LUA_MULTRET, 0) == 0; ++i)
    {
        std::cout << "\nStack on iteration " << i << '\n';
        for (int b = 0, e = lua_gettop(L); b != e; ++b)
            std::cout << '\t' << lua_typename(L, lua_type(L, b + 1)) << '\n';
    }

    std::cout << "Before exit: " << lua_tostring(L, -1);

    return 0;
}

关于您的问题,您期望lua_pcall在每次循环迭代中都调用f两次,但它的行为似乎不是这样的。下面是您提供的程序输出:

Stack before call:
        function
        function
- Function call, stack size: 1, parameter value: 2

Stack on iteration 0
        function
- Function call, stack size: 1, parameter value: 1

Stack on iteration 1
Before exit: attempt to call a nil value

在第一次调用之前,堆栈上有两个函数(可能是f1f2)。

  1. 在第一次调用中,只调用了f2f1仍然留在堆栈上。
  2. 在第二次调用中,只调用了f1。堆栈变为空。
  3. 在第三次调用中,lua_pcall 失败并结束循环。

看起来每次调用时,都会从堆栈中弹出一个函数,所以显然我没有正确理解Lua库的工作原理。这是我(错误地)认为的情况:

  • 在相同的lua_State指针上连续调用luaL_loadstring,会将代码添加到Lua状态中,就好像我们在连接脚本。
  • 在相同的lua_State指针上每次调用lua_pcall,状态保持不变,就像它正在解释加载的脚本。

要执行(保持状态不变)我已经使用luaL_loadstring添加到状态中的代码,您应该怎么做呢?

英文:

This is a simplified version of the code I'm testing:


int f(lua_State *L)
{
	std::cout &lt;&lt; std::format(&quot;- Function call, stack size: {}, parameter value: {}\n&quot;, lua_gettop(L), lua_tointeger(L, 1));
	return 0;
}

int main()
{
	lua_State *const L = luaL_newstate();
	luaL_loadstring(L, &quot;f1(1)&quot;);
	luaL_loadstring(L, &quot;f2(2)&quot;);
	lua_register(L, &quot;f1&quot;, f);
	lua_register(L, &quot;f2&quot;, f);

	std::cout &lt;&lt; &quot;\nStack before call:\n&quot;;
	for (int b = 0, e = lua_gettop(L); b != e; ++b)
		std::cout &lt;&lt; &#39;\t&#39; &lt;&lt; lua_typename(L, lua_type(L, b + 1)) &lt;&lt; &#39;\n&#39;;

	for (int i = 0; lua_pcall(L, 0, LUA_MULTRET, 0) == 0; ++i)
	{
		std::cout &lt;&lt; &quot;\nStack on iteration &quot; &lt;&lt; i &lt;&lt; &#39;\n&#39;;
		for (int b = 0, e = lua_gettop(L); b != e; ++b)
			std::cout &lt;&lt; &#39;\t&#39; &lt;&lt; lua_typename(L, lua_type(L, b + 1)) &lt;&lt; &#39;\n&#39;;
	}

	std::cout &lt;&lt; &quot;Before exit: &quot; &lt;&lt; lua_tostring(L, -1);

	return 0;
}

I was expecting lua_pcall to call f twice each iteration of the loop, but this isn't how is it behaving, I'll paste the program output right below:

Stack before call:
        function
        function
- Function call, stack size: 1, parameter value: 2

Stack on iteration 0
        function
- Function call, stack size: 1, parameter value: 1

Stack on iteration 1
Before exit: attempt to call a nil value

I have two functions on the stack before the first call (they might be f1 and f2).

  1. On the first call only f2 is called. f1 remains on the stack.
  2. On the second call only f1 is called. The stack is empty.
  3. On the third call, lua_pcall fails and ends the loop.

Looks like on each call one function is popped from the stack, so obviously I'm not understanding how the Lua library works. This is what I (wrongly) thought:

  • Consecutive calls to luaL_loadstring over the same lua_State pointer, adds code to the lua state, as if we were concatenating scripts.
  • Each call to lua_pcall over the same lua_State pointer, the state remains unchanged as if it was interpreting the loaded script.

What should I do in order to just execute (keep the state unchanged) the code I've added to the state with luaL_loadstring?

答案1

得分: 1

在每次调用时,似乎从堆栈中弹出一个函数

这是正确的,而且你的代码基本上是正确的。问题是在执行 f1(1) 之后,lua_gettop(L) 变为 0,因此在下一次调用 lua_pcall 函数时,实际上调用了一个不正确的地址。你可以先检查堆栈顶部:

for (int i = 0; lua_gettop(L) > 0 && lua_pcall(L, 0, LUA_MULTRET, 0) == 0; ++i)

lua_pcall 函数中有一个 api_checknelems 行,但默认情况下它不执行任何操作,你需要在构建 Lua 库时定义 LUAI_ASSERT

出于同样的原因,你不应再尝试获取一个字符串:

std::cout << "Before exit: "; // << lua_tostring(L, -1);

为了只执行(保持状态不变)我使用 luaL_loadstring 添加到状态中的代码,我应该怎么做?

你可以使用 lua_pushvalue 在调用之前复制函数。

for (int i = 0; lua_pushvalue(L, -1), lua_pcall(L, 0, LUA_MULTRET, 0) == 0; ++i)
英文:

> Looks like on each call one function is popped from the stack

This is right, and your code is basically fine, the problem is after f1(1) is executed lua_gettop(L) becomes 0, so the next call to the lua_pcall function, you actually call an incorrect address. You may check the top first:

for (int i = 0; lua_gettop(L) &gt; 0 &amp;&amp; lua_pcall(L, 0, LUA_MULTRET, 0) == 0; ++i)

There is a api_checknelems line in the lua_pcall function, but by default it does nothing, you need to define LUAI_ASSERT while building the lua library.

For the same reason, you should not try to obtain a string anymore

std::cout &lt;&lt; &quot;Before exit: &quot;; // &lt;&lt; lua_tostring(L, -1);

> What should I do in order to just execute (keep the state unchanged) the code I've added to the state with luaL_loadstring?

You can use lua_pushvalue to duplicate a function before calling it.

for (int i = 0; lua_pushvalue(L, -1), lua_pcall(L, 0, LUA_MULTRET, 0) == 0; ++i)

huangapple
  • 本文由 发表于 2023年7月13日 21:24:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/76679876.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定