大话lua与c语言的桥梁之luaJIT的ffi库(上)

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

  1. ffi.cdef([[
  2. typedef struct Student {
  3. char no[50];
  4. char name[50];
  5. } Stu;
  6. ]])

注:这个括号()可以去掉

例2:声明一个系统函数(C标准库函数)

  1. ffi.cdef[[
  2. int printf(const char *fmt,...);
  3. ]]

例3:定义一个第三方库的函数或者自定义函数

  1. ffi.cdef[[
  2. long filesize(const char *filename);
  3. ]]
2.ffi.C
  • 功能:调用ffi.cdef中声明的系统函数

  • 用法:ffi.C.函数名(函数参数列表)

  • 参数:即调用的函数的参数

注:它必须是先声明再使用

例如:调用在第一步声明的printf函数(声明的代码在此省略)

  1. 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库

  1. local ffi = require 'ffi'
  2. ffi.cdef[[
  3. void *curl_easy_init();
  4. int curl_easy_setopt(void *curl, int option, ...);
  5. int curl_easy_perform(void *curl);
  6. void curl_easy_cleanup(void *curl);
  7. char *curl_easy_strerror(int code);
  8. ]]
  9. local libcurl = ffi.load('curl')
  10. local curl = libcurl.curl_easy_init()
  11. local CURLOPT_URL = 10002 -- 参考 curl/curl.h 中定义
  12. if curl then
  13. libcurl.curl_easy_setopt(curl, CURLOPT_URL, 'http://www.baidu.com')
  14. res = libcurl.curl_easy_perform(curl)
  15. if res ~= 0 then
  16. print(ffi.string(libcurl.curl_easy_strerror(res)))
  17. end
  18. libcurl.curl_easy_cleanup(curl)
  19. end

注:这些函数的声明可以参照/usr/include/curl/curl.h中的声明

例2:加载自定义函数库

(1)、自定义一个获取文件大小的C函数:cfile.c
  1. #include <stdio.h>
  2. long filesize(char *filename)
  3. {
  4. FILE *fp;
  5. if ( (fp = fopen(filename, "rb")) == NULL) {
  6. printf("open the file failed!\n");
  7. return 0;
  8. }
  9. fseek(fp, 0, SEEK_END);
  10. return ftell(fp);
  11. }
(2)、生成动态链接库:
  1. gcc -fPIC -shared -o cfile.so cfile.c

文件存储在:(可以随意存储,只要能找到即可)

/data/program/capp/io/cfile.so

(3)、在lua代码中使用filesize函数:
  1. local ffi = require 'ffi'
  2. ffi.cdef[[
  3. long filesize(const char *filename);
  4. ]]
  5. local cfile = ffi.load("/data/program/capp/io/cfile.so")
  6. print(type(cfile)) -- userdata
  7. print(cfile.filesize("test.txt")) -- 352LL