Lua 作為嵌入式腳本語言,核心 API 基於虛擬棧交互。調用 Lua 函數遵循固定四步驟:**壓入函數 → 壓入參數 → 執行調用 → 彈出結果**。本文給出標準流程、錯誤處理模式、類型轉換表與可復用封裝函數。
lua_getglobal(L, "func_name") — 將全局函數壓棧lua_pushnumber(L, arg) / lua_pushstring / lua_pushboolean … — 依序壓入參數lua_pcall(L, nargs, nresults, errfunc) — 受保護調用nargs 參數個數,nresults 期望結果數,errfunc 錯誤處理函數索引 (0 表示無)lua_tonumber(L, -1)、lua_tostring(L, -1)… 然後 lua_pop(L, nresults)f(x, y) = (x^2 * sin(y)) / (1 - x)/* 假設 Lua 狀態機 L 已創建並加載腳本 */
double call_f(lua_State *L, double x, double y) {
lua_getglobal(L, "f"); // 1. 函數
lua_pushnumber(L, x); // 2. 參數 1
lua_pushnumber(L, y); // 參數 2
if (lua_pcall(L, 2, 1, 0) != LUA_OK) { // 3. 調用
const char *err = lua_tostring(L, -1);
fprintf(stderr, "Lua error: %s\n", err);
lua_pop(L, 1); // 彈出錯誤信息
return NAN;
}
if (!lua_isnumber(L, -1)) { // 4. 類型檢查
fprintf(stderr, "f() must return a number\n");
lua_pop(L, 1);
return NAN;
}
double z = lua_tonumber(L, -1);
lua_pop(L, 1); // 清理棧
return z;
}
| 操作 | API | 備註 |
|---|---|---|
| 壓入全局函數 | lua_getglobal(L, name) | 同名變量非函數會報錯 |
| 壓入數字 | lua_pushnumber(L, n) | Lua 5.3+ 整數用 lua_pushinteger |
| 壓入字符串 | lua_pushlstring(L, s, len) | 二進制安全 |
| 受保護調用 | lua_pcall(L, nargs, nresults, msgh) | 返回 LUA_OK 成功,否則錯誤碼 |
| 檢查類型 | lua_isnumber/string/boolean/table/function | |
| 取數字 | lua_tonumber(L, idx) | idx=-1 為棧頂 |
| 取字符串 | lua_tolstring(L, idx, &len) | 指針屬於 Lua,勿 free |
| 彈出棧頂 | lua_pop(L, n) | 宏定義 lua_settop(L, -n-1) |
lua_call (會長跳出導致 C 棧展開失敗)。msgh 索引可自定義堆棧追蹤,常用 luaL_traceback。lua_is* 驗證。-- Lua: function g() return 1, "hello", true end
lua_getglobal(L, "g");
lua_pcall(L, 0, 3, 0); // 期望 3 個結果
int n = lua_tonumber(L, -3); // 底部第一個
const char *s = lua_tostring(L, -2);
int b = lua_toboolean(L, -1);
lua_pop(L, 3);
注意:多返回值中第一個最先壓入,棧頂是最後一個。
static int l_sin(lua_State *L) {
double d = luaL_checknumber(L, 1);
lua_pushnumber(L, sin(d));
return 1; // 返回值個數
}
lua_register(L, "c_sin", l_sin); // Lua 中可直接 c_sin(3.14)
C 調用 Lua 核心是棧協議:壓函數、壓參數、lua_pcall、取結果、清棧。封裝通用 call_lua_func 並嚴格檢查類型與錯誤碼,即可構建穩健的 C/Lua 雙向互調層。