2

如果 C 函数调用 lua 函数,并且 lua 函数调用 C API,则它在 lua 调用 C API 时起作用,longjmp 错误

lua_yieldk、lua_callk 和 lua_pcallk 它是如何工作的?

我的 C 代码:

int trace(lua_State *L)
{
    const char *str = luaL_checkstring(L, 1);
    printf("%d:%s\n", GetTickCount(), str);
    return 1;
 }

int pause(lua_State *L)
{
    printf("pause");
    return lua_yield(L, 0);
}

int _tmain(int argc, _TCHAR* argv[])
{
    lua_State *L = luaL_newstate(); 
    luaL_openlibs(L);
    lua_pushcfunction( L, pause );
    lua_setglobal( L, "pause" );
    lua_pushcfunction( L, trace );
    lua_setglobal( L, "trace" );
    if (luaL_loadfile(L, "test.lua"))
       error(L, "cannot run script %s\n", lua_tostring(L,-1));
    lua_resume(L, NULL, 0);
        lua_getglobal(L, "t");
    lua_pcallk(L, 0, 0, 0, 0, 0);
    lua_resume(L, NULL, 0);
    lua_resume(L, NULL, 0);
    lua_resume(L, NULL, 0);
    lua_resume(L, NULL, 0);
    lua_close(L);
    getchar();
    return 0;
}

lua代码

function t()
pause(2)
pause(2)
pause(2)
pause(2)
end
4

1 回答 1

5

您在返回的线程上调用 lua_resume lua_newthread,而不是lua_newstate

因此,在您的代码中,您要么必须将第一个更改lua_resumelua_(p)call

if (luaL_loadfile(L, "test.lua"))
   error(L, "cannot run script %s\n", lua_tostring(L,-1));
lua_pcall(L, 0, 0, 0);

或交换luaL_loadfileluaL_dofile

if (luaL_dofile(L, "test.lua"))
   error(L, "cannot run script %s\n", lua_tostring(L,-1));
//lua_resume(L, NULL, 0); Not necessary anymore

我与在t这里设置全局的效率无关。

现在到问题的重点:

  • 首先,每次调用lua_callk, lua_pcallkor都lua_yieldk需要接收一个延续函数作为参数。在您的情况下,它是 0。实际上,lua_yieldk可以将 0 作为延续函数,但随后将控制权传递回 Lua 脚本,在该脚本中调用了 C 函数。
  • 接下来,对这些函数的任何调用都必须在协程线程中进行,而不是在主线程中进行。
  • 最后,你不能跨越 C 调用边界。也就是说,当您调用lua_pcallk并且 pcallk 正在调用的块产生时,将执行延续函数。但是,您不能lua_pcallk调用 Lua 函数,而该函数又调用产生(pause在您的示例中)的 C 函数。这是禁止的。

一个例子lua_pcallk

int cont(lua_State *L)
{
    getchar();
    return 0;
}

int pcallktest(lua_State *L)
{
    luaL_loadstring(L, "yield()");
    int test = lua_pcallk(L, 0, 0, 0, 0, cont);
    return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);
    lua_State *T = lua_newthread(L);

    luaL_loadfile(T, "Test.lua");
    lua_pushcfunction(T, pcallktest);
    lua_resume(T, NULL, 1);
    return 0;
}

卢阿代码:

local pcallktest = ...
pcallktest()

现在这段代码从文件“Test.lua”开始一个新的协程。Lua 代码调用 C 函数pcallktest,该函数又调用lua_pcallk另一个 Lua 函数,该函数只是产生。当 yield 发生时,执行跳转 (longjmp) 到cont作为参数提供给lua_pcallk. 当cont函数返回时,协程执行结束并lua_resume_tmain返回值开始。

一个例子lua_yieldk

int cont(lua_State *L)
{
    getchar();
    return 0;
}

int yieldktest(lua_State *L)
{
    return lua_yieldk(L, 0, 0, cont);
}
int _tmain(int argc, _TCHAR* argv[])
{
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);
    lua_State *T = lua_newthread(L);

    luaL_loadfile(T, "Test.lua");
    lua_pushcfunction(T, yieldktest);
    lua_resume(T, NULL, 1);
    lua_resume(T, NULL, 0);
    return 0;
}

卢阿代码:

local yieldktest = ...
yieldktest()

该位依次执行从 C 函数 ( yieldktest) 中产生的协程。当协程然后恢复时(第二个lua_resume),控制权被传递回延续函数cont,它作为 的延续执行yieldktest

这些示例不处理lua_getctx和堆栈状态,而只是演示这些函数的机制。

于 2013-05-07T14:25:59.783 回答