Lua 與 C 通過虛擬棧交互,C 模塊需在特定時機調用 Lua 定義的回調函數。主要有兩種模式:註冊表引用與全局環境查找。
利用 luaL_ref 將棧頂函數存入註冊表,返回整數索引;調用時用 lua_rawgeti 取出再 lua_call。
static int lua_callback = LUA_REFNIL; // 全局靜態變量持有引用
static int setnotify(lua_State* L) {
lua_callback = luaL_ref(L, LUA_REGISTRYINDEX); // 彈棧頂函數,存註冊表
return 0;
}
static int testnotify(lua_State* L) {
lua_rawgeti(L, LUA_REGISTRYINDEX, lua_callback); // 取出函數壓棧
lua_call(L, 0, 0); // 調用:0參數,0返回值
return 0;
}
function callback() print("Callback from registry") end
cb.setnotify(callback) -- 註冊
cb.testnotify() -- 觸發回調
約定回調函數固定全局名(如 defcallback),C 代碼直接 lua_getglobal 查找調用。
static int testenv(lua_State* L) {
lua_getglobal(L, "defcallback"); // 從 _G 取函數
lua_call(L, 0, 0);
return 0;
}
local function my_callback() print("My logic") end
function defcallback() my_callback() end -- 適配 C 固定名
cb.testenv()
cb.c#include <stdio.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
static int lua_callback = LUA_REFNIL;
static int setnotify(lua_State* L) {
lua_callback = luaL_ref(L, LUA_REGISTRYINDEX);
return 0;
}
static int testnotify(lua_State* L) {
lua_rawgeti(L, LUA_REGISTRYINDEX, lua_callback);
lua_call(L, 0, 0);
return 0;
}
static int testenv(lua_State* L) {
lua_getglobal(L, "defcallback");
lua_call(L, 0, 0);
return 0;
}
static const luaL_Reg cblib[] = {
{"setnotify", setnotify},
{"testnotify", testnotify},
{"testenv", testenv},
{NULL, NULL}
};
int luaopen_cb(lua_State* L) {
luaL_register(L, "cb", cblib);
return 1;
}
| 維度 | 註冊表引用 | 全局環境查找 |
|---|---|---|
| 靈活性 | 高(動態註冊任意函數) | 低(固定全局名) |
| 命名空間污染 | 無 | 需佔用全局名 |
| 多回調支持 | 易擴展(多索引) | 需約定多名字 |
| 適用場景 | 通用庫、插件系統 | 簡單腳本、快速原型 |
luaL_ref 返回 LUA_REFNIL 表示無效引用,調用前需判空。lua_pcall 保護調用棧。