Lua调用C的动态库步骤及接口分析

Lua调用C的动态库

C语言可以完成一些lua不好实现的功能,当程序主体使用lua完成时,便需要掌握该技巧调用C来帮助我们达到目的,通过调用C的动态库简化操作流程。

大致流程如下:

  1. 使用C语言编写方法提供给lua调用
  2. 将C文件打包成动态库
  3. lua导入动态库,直接调用里面的函数

文章目录

    • Lua调用C的动态库
      • 准备工作
      • 使用C语言编写方法
      • 将C文件打包成动态库
      • lua文件中导入动态库并调用其中函数
      • 多个返回值方法编写示例
      • 常用的lua接口
        • 获取函数传入参数
        • 设置函数返回值

准备工作

确保系统安装lua5.4版本。
首先在目录下创建一个func.c文件和一个call.lua文件,Makefile文件可有可无,主要方便后面编译动态库可以直接使用make。

Lua调用C的动态库步骤及接口分析_第1张图片

使用C语言编写方法

编写需要加入头文件,如下:

#include     	// Lua基础函数库,提供lua_前缀函数
#include     // 辅助库(可以不加),luaL_前缀函数,利用lua.h实现的更高层的抽象

最重要的是定义一个类似main的主函数:luaopen_*函数,在其中注册所要添加方法

  • 在luaopen_func中注册一个函数add,此后lua导入后可以直接通过add调用该函数。
  • 函数返回值必须是int类型,返回值是返回参数的个数。
  • 函数中固定使用参数lua_State,传入参数和返回参数都需要它。
#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;
}

需要注意的地方:

  • 如例子中使用的是luaopen_func,func就是我们之后在lua中导入的库名称,即require “func”,如果这个名字带有下划线如luaopen_func_test,那么lua中导入方法为require “func.test”

将C文件打包成动态库

可以使用命令直接打包,也可以写进makefile中,方便之后同时打包多个动态库

  • 使用一条命令打包单个动态库gcc func.c -o func.so -fPIC -shared,注意func.so命名跟luaopen_后面的命名有关,并不是跟func.c文件名有关。
  • 使用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)

打包后如下图
Lua调用C的动态库步骤及接口分析_第2张图片

lua文件中导入动态库并调用其中函数

在同级的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)

运行结果如下:
Lua调用C的动态库步骤及接口分析_第3张图片

以上演示的是简单的单个返回值例子,如果有多个不同类型的返回值该如何去执行呢?

多个返回值方法编写示例

注册一个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)

运行结果如下:
Lua调用C的动态库步骤及接口分析_第4张图片

常用的lua接口

获取函数传入参数
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,此处不多做介绍。

你可能感兴趣的:(开发语言,lua,c语言,开发语言)