C语言可以完成一些lua不好实现的功能,当程序主体使用lua完成时,便需要掌握该技巧调用C来帮助我们达到目的,通过调用C的动态库简化操作流程。
大致流程如下:
确保系统安装lua5.4版本。
首先在目录下创建一个func.c文件和一个call.lua文件,Makefile文件可有可无,主要方便后面编译动态库可以直接使用make。
编写需要加入头文件,如下:
#include // Lua基础函数库,提供lua_前缀函数
#include // 辅助库(可以不加),luaL_前缀函数,利用lua.h实现的更高层的抽象
最重要的是定义一个类似main的主函数:luaopen_*函数,在其中注册所要添加方法
#include
#include
// 注意,提供给lua调用的方法必须是static int类型的函数
// 返回值表示该函数返回值的个数
// 函数返回值通过lua_push放入堆栈中
static int add(lua_State *L)
{
printf("C -- add:\n");
// 解析参数
int a = lua_tonumber(L, 1);
int b = lua_tonumber(L, 2);
printf("a is %d\nb is %d\n", a, b);
// 返回给lua的值
lua_pushnumber(L, a + b);
return 1;
}
int luaopen_func(lua_State *L)
{
// 注册方法提供调用,前面是调用名称,后面是调用处理函数
lua_register(L, "add", add);
return 0;
}
需要注意的地方:
可以使用命令直接打包,也可以写进makefile中,方便之后同时打包多个动态库
CC := gcc
SRCS := $(wildcard *.c)
TARGETS := $(patsubst %.c, %.so, $(SRCS))
CFLAGS := -fPIC -shared
.PHONY: all clean
all: $(TARGETS)
$(TARGETS):%.so:%.c
$(CC) $< -o $@ $(CFLAGS)
clean:
rm -rf $(TARGETS)
在同级的call.lua中编写以下代码,直接调用func.so中的函数add
m=require('func')
print("require result:")
print(m)
local a, b = 3, 4
local res = add(a,b)
print("res : " .. res)
以上演示的是简单的单个返回值例子,如果有多个不同类型的返回值该如何去执行呢?
注册一个judge方法,传入name和age和now_year,判断出生年份,返回name+出生年份。
在func.c文件中增加一个judge函数,如下
static int judge(lua_State *L)
{
// 解析参数
const char *name = lua_tolstring(L, 1, NULL);
int age = lua_tonumber(L, 2);
int now_year = lua_tonumber(L, 3);
int born_year = now_year - age;
lua_pushstring(L, name);
lua_pushnumber(L, born_year);
// 返回2表示该函数有两个返回值
return 2;
}
// 同时需要在主函数中注册judge方法提供调用
int luaopen_func(lua_State *L)
{
// 注册方法提供调用,前面是调用名称,后面是调用处理方法
lua_register(L, "add", add);
lua_register(L, "judge", judge);
return 0;
}
在call.lua中添加响应的调用语句
local name, age, now_year = "glh", 22, 2022
local newname, born_year = judge(name, age, now_year)
print("newname : " .. newname)
print("born_year : " .. born_year)
int (lua_isnumber) (lua_State *L, int idx); // 判断传入参数是不是数字
int (lua_isstring) (lua_State *L, int idx); // 判断传入参数是不是字符串
int (lua_iscfunction) (lua_State *L, int idx); // 判断传入参数是不是函数
int (lua_isuserdata) (lua_State *L, int idx); // 判断传入参数是不是table?
int (lua_type) (lua_State *L, int idx); //
const char *(lua_typename) (lua_State *L, int tp); //
int (lua_equal) (lua_State *L, int idx1, int idx2); // 判断传入参数是不是相等
int (lua_rawequal) (lua_State *L, int idx1, int idx2); //
int (lua_lessthan) (lua_State *L, int idx1, int idx2); //
lua_Number (lua_tonumber) (lua_State *L, int idx); // 获取传入的number参数
lua_Integer (lua_tointeger) (lua_State *L, int idx); // 获取传入的int参数
int (lua_toboolean) (lua_State *L, int idx); // 获取传入的bool参数
const char *(lua_tolstring) (lua_State *L, int idx, size_t *len); // 获取传入的string参数,注意是传长度地址!
size_t (lua_objlen) (lua_State *L, int idx);
lua_CFunction (lua_tocfunction) (lua_State *L, int idx);
void *(lua_touserdata) (lua_State *L, int idx);
lua_State *(lua_tothread) (lua_State *L, int idx);
const void *(lua_topointer) (lua_State *L, int idx);
void (lua_pushnil) (lua_State *L);
void (lua_pushnumber) (lua_State *L, lua_Number n);
void (lua_pushinteger) (lua_State *L, lua_Integer n);
void (lua_pushlstring) (lua_State *L, const char *s, size_t l);
void (lua_pushstring) (lua_State *L, const char *s);
const char *(lua_pushvfstring) (lua_State *L, const char *fmt, va_list argp);
const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
void (lua_pushboolean) (lua_State *L, int b);
void (lua_pushlightuserdata) (lua_State *L, void *p);
int (lua_pushthread) (lua_State *L);
这边只举了一些常用的获取参数和设置返回值的方法,更多详细方法可以参考/usr/include/lua/lua.h头文件。
本文只简单分析了lua.h包含的一些接口并且成功执行调用,更深入的用法可以使用lauxlib.h,此处不多做介绍。