Lua C API的核心是虚拟栈(Lua Stack),它作为Lua和C之间的数据交换桥梁。所有数据传递都通过栈进行:C函数从栈获取参数,将结果压入栈。栈是LIFO(后进先出)结构,索引从1开始,负数表示从栈顶开始的位置。这种设计使得C和Lua之间的交互类型安全且高效,同时简化了内存管理(Lua自动管理栈上对象的生命周期)。
// mylib.c - 简单的C扩展
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
// C函数:返回两个数的和
static int l_add(lua_State *L) {
double a = luaL_checknumber(L, 1); // 获取第一个参数
double b = luaL_checknumber(L, 2); // 获取第二个参数
lua_pushnumber(L, a + b); // 压入结果
return 1; // 返回值数量
}
// 注册函数
static const struct luaL_Reg mylib[] = {
{"add", l_add},
{NULL, NULL}
};
// 模块入口
int luaopen_mylib(lua_State *L) {
luaL_newlib(L, mylib);
return 1;
}
// 编译: gcc -shared -fPIC -o mylib.so mylib.c -I/usr/include/lua5.3 -llua5.3
-- 加载C模块
local mylib = require("mylib")
-- 调用C函数
local result = mylib.add(10, 20)
print(result) -- 30
// 栈是Lua和C交互的核心
static int stack_demo(lua_State *L) {
// 压入值
lua_pushnil(L); // 压入nil
lua_pushboolean(L, 1); // 压入true
lua_pushnumber(L, 3.14); // 压入数字
lua_pushstring(L, "hello"); // 压入字符串
// 获取栈顶索引
int top = lua_gettop(L);
printf("栈中有 %d 个元素\n", top);
// 访问栈元素(索引从1开始,负数从栈顶开始)
double num = lua_tonumber(L, -2); // 获取倒数第二个
const char *str = lua_tostring(L, -1); // 获取栈顶
// 弹出元素
lua_pop(L, 2); // 弹出2个元素
return 0;
}
// 常用栈操作函数:
// lua_pushvalue(L, index) - 复制指定索引的值到栈顶
// lua_remove(L, index) - 删除指定索引的值
// lua_insert(L, index) - 将栈顶值插入到指定位置
// lua_replace(L, index) - 用栈顶值替换指定位置
static int type_demo(lua_State *L) {
// 检查类型
if (lua_isnumber(L, 1)) {
printf("参数1是数字\n");
}
if (lua_isstring(L, 2)) {
printf("参数2是字符串\n");
}
if (lua_istable(L, 3)) {
printf("参数3是表\n");
}
// 强制类型检查(失败会报错)
int num = luaL_checkinteger(L, 1);
const char *str = luaL_checkstring(L, 2);
// 可选参数
int opt = luaL_optinteger(L, 3, 100); // 默认值100
// 类型转换
double d = lua_tonumber(L, 1);
const char *s = lua_tostring(L, 2);
int b = lua_toboolean(L, 3);
return 0;
}
static int table_demo(lua_State *L) {
// 创建新表
lua_newtable(L);
// 设置字段: table["name"] = "张三"
lua_pushstring(L, "张三");
lua_setfield(L, -2, "name");
// 设置数组元素: table[1] = 100
lua_pushinteger(L, 100);
lua_rawseti(L, -2, 1);
// 读取字段
lua_getfield(L, -1, "name");
const char *name = lua_tostring(L, -1);
lua_pop(L, 1);
// 读取数组元素
lua_rawgeti(L, -1, 1);
int value = lua_tointeger(L, -1);
lua_pop(L, 1);
// 遍历表
lua_pushnil(L); // 第一个key
while (lua_next(L, -2) != 0) {
// 栈: ... table key value
const char *key = lua_tostring(L, -2);
const char *val = lua_tostring(L, -1);
printf("%s = %s\n", key, val);
lua_pop(L, 1); // 弹出value,保留key
}
return 1; // 返回表
}
// 从C调用Lua函数
void call_lua_function(lua_State *L) {
// 假设全局有函数: function add(a, b) return a + b end
lua_getglobal(L, "add"); // 获取函数
lua_pushinteger(L, 10); // 参数1
lua_pushinteger(L, 20); // 参数2
// lua_pcall(L, nargs, nresults, errfunc)
if (lua_pcall(L, 2, 1, 0) != LUA_OK) {
const char *err = lua_tostring(L, -1);
printf("错误: %s\n", err);
lua_pop(L, 1);
return;
}
// 获取结果
int result = lua_tointeger(L, -1);
printf("结果: %d\n", result);
lua_pop(L, 1);
}
// 定义C结构体
typedef struct {
int x;
int y;
} Point;
// 创建userdata
static int point_new(lua_State *L) {
int x = luaL_checkinteger(L, 1);
int y = luaL_checkinteger(L, 2);
// 分配userdata
Point *p = (Point *)lua_newuserdata(L, sizeof(Point));
p->x = x;
p->y = y;
// 设置元表
luaL_getmetatable(L, "Point");
lua_setmetatable(L, -2);
return 1;
}
// 方法:获取距离
static int point_distance(lua_State *L) {
Point *p = (Point *)luaL_checkudata(L, 1, "Point");
double dist = sqrt(p->x * p->x + p->y * p->y);
lua_pushnumber(L, dist);
return 1;
}
// 垃圾回收
static int point_gc(lua_State *L) {
Point *p = (Point *)luaL_checkudata(L, 1, "Point");
printf("Point被回收: (%d, %d)\n", p->x, p->y);
return 0;
}
// 注册Point类
int luaopen_point(lua_State *L) {
// 创建元表
luaL_newmetatable(L, "Point");
// 设置__index指向自己
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
// 注册方法
lua_pushcfunction(L, point_distance);
lua_setfield(L, -2, "distance");
// 注册__gc
lua_pushcfunction(L, point_gc);
lua_setfield(L, -2, "__gc");
// 创建模块表
lua_newtable(L);
lua_pushcfunction(L, point_new);
lua_setfield(L, -2, "new");
return 1;
}
-- 使用Point类
local point = require("point")
local p = point.new(3, 4)
print(p:distance()) -- 5.0
static int safe_divide(lua_State *L) {
double a = luaL_checknumber(L, 1);
double b = luaL_checknumber(L, 2);
if (b == 0) {
return luaL_error(L, "除数不能为0");
}
lua_pushnumber(L, a / b);
return 1;
}
// 使用luaL_argcheck
static int check_demo(lua_State *L) {
int age = luaL_checkinteger(L, 1);
luaL_argcheck(L, age >= 0 && age <= 150, 1, "年龄范围无效");
return 0;
}
// 注册多个函数
static const struct luaL_Reg mathlib[] = {
{"add", l_add},
{"subtract", l_subtract},
{"multiply", l_multiply},
{"divide", l_divide},
{NULL, NULL}
};
int luaopen_mathlib(lua_State *L) {
// 创建模块表
luaL_newlib(L, mathlib);
// 添加常量
lua_pushnumber(L, 3.14159);
lua_setfield(L, -2, "PI");
return 1;
}
// main.c - 在C程序中嵌入Lua
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
int main(void) {
// 创建Lua状态机
lua_State *L = luaL_newstate();
// 加载标准库
luaL_openlibs(L);
// 执行Lua代码
if (luaL_dostring(L, "print('Hello from Lua!')") != LUA_OK) {
fprintf(stderr, "错误: %s\n", lua_tostring(L, -1));
lua_close(L);
return 1;
}
// 执行Lua文件
if (luaL_dofile(L, "script.lua") != LUA_OK) {
fprintf(stderr, "错误: %s\n", lua_tostring(L, -1));
}
// 设置全局变量
lua_pushinteger(L, 42);
lua_setglobal(L, "answer");
// 获取全局变量
lua_getglobal(L, "answer");
int answer = lua_tointeger(L, -1);
printf("answer = %d\n", answer);
// 关闭Lua
lua_close(L);
return 0;
}
// 编译: gcc -o main main.c -llua5.3 -lm -ldl
// strlib.c
#include <lua.h>
#include <lauxlib.h>
#include <string.h>
#include <ctype.h>
// 转大写
static int str_upper(lua_State *L) {
size_t len;
const char *str = luaL_checklstring(L, 1, &len);
char *result = malloc(len + 1);
for (size_t i = 0; i < len; i++) {
result[i] = toupper(str[i]);
}
result[len] = '\0';
lua_pushstring(L, result);
free(result);
return 1;
}
// 反转字符串
static int str_reverse(lua_State *L) {
size_t len;
const char *str = luaL_checklstring(L, 1, &len);
char *result = malloc(len + 1);
for (size_t i = 0; i < len; i++) {
result[i] = str[len - 1 - i];
}
result[len] = '\0';
lua_pushstring(L, result);
free(result);
return 1;
}
static const struct luaL_Reg strlib[] = {
{"upper", str_upper},
{"reverse", str_reverse},
{NULL, NULL}
};
int luaopen_strlib(lua_State *L) {
luaL_newlib(L, strlib);
return 1;
}
# Makefile
CC = gcc
CFLAGS = -Wall -fPIC -I/usr/include/lua5.3
LDFLAGS = -shared -llua5.3
all: mylib.so strlib.so point.so
mylib.so: mylib.c
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
strlib.so: strlib.c
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
point.so: point.c
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lm
clean:
rm -f *.so
install:
cp *.so /usr/local/lib/lua/5.3/
.PHONY: all clean install