FFI库是lua与C语言的桥梁,可以在lua代码中直接使用C函数或者C数据结构,而不需要通过lua的C扩展来实现,确实很方便。
一、使用FFI库的步骤
1、加载FFI模块
2、使用ffi.cdef声明C函数或者C的数据结构
3、使用ffi模块其他函数
二、FFI库的相关词汇
- cdecl:一个抽象的C类型定义(其实是一个lua字符串)
- ctype:一个C类型对象
- cdata:一个C数据对象
- ct:一个C类型格式,就是一个模板对象,可能是cdecl,cdata,ctype
- cb:一个回调对象
- VLA:一个可变长度的数组
- VLS:一个可变长度的结构体
三、FFI模块的API解读
1、ffi.cdef
功能:声明C函数或C数据结构
用法:ffi.cdef(cdecl)
参数说明:cdecl:C声明函数语句或C数据结构定义语句
注意点:
这里的数据结构可以是结构体、枚举或共同体Union
- 这里的函数可以是C标准库函数,或者其他第三方库函数,也可以是自定义函数(注意这些函数的参数的声明与原函数应该保持一致)
- 这里只是函数声明,不是函数定义
如:
例1:定义一个结构体:struct Student
ffi.cdef([[
typedef struct Student {
char no[50];
char name[50];
} Stu;
]])
注:这个括号()可以去掉
例2:声明一个系统函数(C标准库函数)
ffi.cdef[[
int printf(const char *fmt,...);
]]
例3:定义一个第三方库的函数或者自定义函数
ffi.cdef[[
long filesize(const char *filename);
]]
2.ffi.C
功能:调用ffi.cdef中声明的系统函数
用法:ffi.C.函数名(函数参数列表)
参数:即调用的函数的参数
注:它必须是先声明再使用
例如:调用在第一步声明的printf函数(声明的代码在此省略)
ffi.C.printf("hello world!\n")
3、ffi.load
功能:加载第三方库或自定义库(非标准库)
用法:ffi.load(name[,global])
参数:
name为模块名或者模块文件所在位置
global为是否为库添加全局命名空间
注:这个函数库一般是动态链接库(Linux中也叫共享函数库,以.so为后缀,Windows下以.dll为后缀)
- 当name是模块名时,表示加载系统函数库,可以省略后缀.so,也可以省图前缀lib(如linux有libcurl.so这个函数库,实现curl的功能),它默认查找的是共享函数库的目录,可以打开/etc/ld.so.conf查找系统函数库的位置
- 当name是文件路径时,需要指明文件完整路径
例1:加载第三方函数库(系统函数库)curl库
local ffi = require 'ffi'
ffi.cdef[[
void *curl_easy_init();
int curl_easy_setopt(void *curl, int option, ...);
int curl_easy_perform(void *curl);
void curl_easy_cleanup(void *curl);
char *curl_easy_strerror(int code);
]]
local libcurl = ffi.load('curl')
local curl = libcurl.curl_easy_init()
local CURLOPT_URL = 10002 -- 参考 curl/curl.h 中定义
if curl then
libcurl.curl_easy_setopt(curl, CURLOPT_URL, 'http://www.baidu.com')
res = libcurl.curl_easy_perform(curl)
if res ~= 0 then
print(ffi.string(libcurl.curl_easy_strerror(res)))
end
libcurl.curl_easy_cleanup(curl)
end
注:这些函数的声明可以参照/usr/include/curl/curl.h中的声明
例2:加载自定义函数库
(1)、自定义一个获取文件大小的C函数:cfile.c
#include <stdio.h>
long filesize(char *filename)
{
FILE *fp;
if ( (fp = fopen(filename, "rb")) == NULL) {
printf("open the file failed!\n");
return 0;
}
fseek(fp, 0, SEEK_END);
return ftell(fp);
}
(2)、生成动态链接库:
gcc -fPIC -shared -o cfile.so cfile.c
文件存储在:(可以随意存储,只要能找到即可)
/data/program/capp/io/cfile.so
(3)、在lua代码中使用filesize函数:
local ffi = require 'ffi'
ffi.cdef[[
long filesize(const char *filename);
]]
local cfile = ffi.load("/data/program/capp/io/cfile.so")
print(type(cfile)) -- userdata
print(cfile.filesize("test.txt")) -- 352LL