如何在C函数中生成Lua脚本

当 Lua 调用 C API 时,它能正常工作。但若一个 C 函数调用了 Lua 函数,并且该 Lua 函数调用了 C API,则会出现 longjmp 错误。

lua_yieldklua_callklua_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
点赞
用户2198692
用户2198692

你应该在由 lua_newthread 返回的线程上调用 lua_resume,而不是在 lua_newstate 上调用。

因此,在你的代码中,你要么将第一个 lua_resume 更改为 lua_(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_loadfile 替换为 luaL_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_callklua_pcallklua_yieldk 都需要接收一个续传函数作为参数。在你的情况下,它是 0。实际上,lua_yieldk 可以接受 0 作为续传函数,但那样控制权会被传回到 Lua 脚本,即 C 函数的调用点。
  • 其次,任何对这些函数的调用都必须在协程线程中进行,而不是在主线程中。
  • 最后,你不能在 C 调用边界上进行暂停。也就是说,当你调用 lua_pcallk 并且该调用调用的块暂停时,将执行续传函数。但是,你不能使 lua_pcallk 调用一个再调用 C 函数(在你的示例中是 pause)的 Lua 函数进行暂停。这是被禁止的。

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

Lua 代码:

local pcallktest = ...
pcallktest()

现在,这段代码从文件 "Test.lua" 开始一个新的协程。Lua 代码调用 C 函数 pcallktest,这个函数又在另一个 Lua 函数上调用 lua_pcallk,该函数只会暂停。当发生暂停时,执行会跳转(longjmp)到作为 lua_pcallk 参数提供的 cont 函数。当 cont 函数返回时,协程执行结束,并且 _tmain 中的 lua_resume 返回。

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

Lua 代码:

local yieldktest = ...
yieldktest()

这段代码从 C 函数 yieldktest 执行协程并暂停。当协程恢复(第二个 lua_resume)时,控制权被传回续传函数 cont,它作为 yieldktest 的续传函数执行。

这些示例没有涉及 lua_getctx 和堆栈状态,但仅演示了这些函数的机制。

2013-05-07 14:25:59