Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

从 C 调用 Lua

C 代码可以加载并执行 Lua 代码,也可以直接调用 Lua 函数。

执行 Lua 代码

最简单的方式是使用辅助库的 luaL_dostringluaL_dofile(第 14 章会讲更底层的加载机制):

// 执行字符串
if (luaL_dostring(L, "return 1 + 1") != LUA_OK) {
    // 出错,错误信息在栈顶
    fprintf(stderr, "%s\n", lua_tostring(L, -1));
    lua_pop(L, 1);
}

它们实际上是两步的封装:先加载,再执行。

lua_pcall 用于在保护模式下调用栈上的函数,这是 C 与 Lua 交互的核心机制。第 6 章会深入讲解错误处理。

调用 Lua 函数

假设 Lua 全局环境中有一个函数 add

-- config.lua
function add(a, b)
    return a + b
end

在 C 中调用它:

// 1. 将函数压栈
// lua_getglobal 从全局表读取变量并压入栈(第 5 章会讲 table 操作)
lua_getglobal(L, "add");

// 2. 压入参数
lua_pushinteger(L, 10);
lua_pushinteger(L, 20);

// 3. 调用: 2 个参数, 1 个返回值
if (lua_pcall(L, 2, 1, 0) == LUA_OK) {
    lua_Integer result = lua_tointeger(L, -1);
    lua_pop(L, 1);       // 弹出返回值 
} else {
    fprintf(stderr, "error: %s\n", lua_tostring(L, -1));
    lua_pop(L, 1);
}

pcall 参数详解

int lua_pcall(lua_State *L, int nargs, int nresults, int msgh);
参数含义
nargs参数个数(必须在栈顶紧挨着函数)
nresults期望的返回值个数,或 LUA_MULTRET(全部)
msgh错误处理函数的栈索引,0 表示无

调用前栈布局:[ ... | function | arg1 | arg2 | ... | argN ](栈顶是最后一个参数)

调用成功后,函数和参数被弹出,返回值留在栈上。

读取 Lua 返回值

如果 Lua 函数返回多个值:

lua_getglobal(L, "multi_return");
lua_pcall(L, 0, LUA_MULTRET, 0);
int nrets = lua_gettop(L);   // 计算返回了多少个值

完整代码


#include <assert.h>
#include <lauxlib.h>
#include <lua.h>
#include <lualib.h>
#include <stdio.h>

int main(void) {
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    luaL_dostring(L, "return 1 + 1");
    assert(lua_tointeger(L, -1) == 2);
    lua_pop(L, 1);

    luaL_dostring(L, "function add(a, b) return a + b end");

    lua_getglobal(L, "add");
    lua_pushinteger(L, 10);
    lua_pushinteger(L, 20);
    lua_pcall(L, 2, 1, 0);
    assert(lua_tointeger(L, -1) == 30);
    lua_pop(L, 1);

    luaL_dostring(L, "function pair() return 1, 2 end");

    lua_getglobal(L, "pair");
    lua_pcall(L, 0, LUA_MULTRET, 0);
    int nrets = lua_gettop(L);
    assert(nrets == 2);
    assert(lua_tointeger(L, 1) == 1);
    assert(lua_tointeger(L, 2) == 2);
    lua_pop(L, nrets);

    lua_close(L);
    puts("03-call-lua: ok");
    return 0;
}