本文参考了luajit的官方文档编写。文档的结构也基本上是按照官网的来。
luajit FFI是一个简化的调用c库函数的途径。目的是简化lua和c语言之间调用的繁琐的push和pop。如果你已经有一个c语言编写的一个库了,可以不改代码的前提下,使用lua直接调用。
开胃菜
调用函数
先使用c编写一个工程,对外部提供一个函数
int c_add(int a, int b);
编写makefile将其编译成一个libc_utils.so库文件出来。
在lua中定义这个c函数并且加载这个so库。
local ffi = require("ffi")
ffi.cdef[[
int c_add(int a,int b);
]]
local cutils = ffi.load('libc_utils')
print(cutils.c_add(200,50))
如果是在windows版本上,可以直接来执行这个命令:
local ffi = require("ffi")
ffi.cdef[[
int MessageBoxA(void *w, const char *txt, const char *cap, int type);
]]
ffi.C.MessageBoxA(nil, "Hello world!", "Test", 0)
将会直接调出一个dialog出来。
使用数据结构
在c库里面定义计算向量相关的函数。我们在代码里面实现一个向量的点×
概念
点乘(Dot Product)
几何意义:
A·B = |A| |B| cos(θ).
θ是向量A和向量B见的夹角。
计算公式:
V1( x1, y1) V2(x2, y2) = x1*x2 + y1*y2
代码实现
typedef struct { uint8_t x, y; } position;
int dot_produce(position *a, position *b);
int dot_produce(position *a, position *b) {
int ret = (a->x) * (b->x) + (a->y * b->y);
return ret;
}
lua中调用
local ffi = require("ffi")
ffi.cdef[[
typedef struct { uint8_t x, y; } position;
int dot_produce(position *a, position *b);
]]
local cutils = ffi.load('libc_utils')
local pos = ffi.new("position[?]", 2)
pos[0].x=10
pos[0].y=10
pos[1].x=500
pos[1].y=300
print(cutils.dot_produce(pos[0],pos[1]))
进阶
调用zlib库
local ffi = require("ffi")
1 ffi.cdef[[
unsigned long compressBound(unsigned long sourceLen);
int compress2(uint8_t *dest, unsigned long *destLen,
const uint8_t *source, unsigned long sourceLen, int level);
int uncompress(uint8_t *dest, unsigned long *destLen,
const uint8_t *source, unsigned long sourceLen);
]]
2 local zlib = ffi.load(ffi.os == "Windows" and "zlib1" or "z")
local function compress(txt)
3 local n = zlib.compressBound(#txt)
local buf = ffi.new("uint8_t[?]", n)
4 local buflen = ffi.new("unsigned long[1]", n)
local res = zlib.compress2(buf, buflen, txt, #txt, 9)
assert(res == 0)
5 return ffi.string(buf, buflen[0])
end
local function uncompress(comp, n)
local buf = ffi.new("uint8_t[?]", n)
local buflen = ffi.new("unsigned long[1]", n)
local res = zlib.uncompress(buf, buflen, comp, #comp)
assert(res == 0)
return ffi.string(buf, buflen[0])
end
-- Simple test code.
local txt = string.rep("abcd", 1000)
print("Uncompressed size: ", #txt)
local c = compress(txt)
print("Compressed size: ", #c)
local txt2 = uncompress(c, #txt)
assert(txt2 == txt)
1.将zlib中的函数方法都定义到ffi接口中;
2.开始加载zlib库的C库文件;
3.通过compressBound函数估算压缩包结果的size;并且new一块n大小的内存。
4.新建一个unsigned long的变量用于提供给compress2函数中作为返回,压缩之后内存块的长度。
调用compress2函数,将内存块加压。
5.将返回的内存块通过ffi.string转换成lua中的内存块。
后面的是他的逆过程。
在这段代码里面还是演示了一些功能:
关于ffi.new的用法。这个里面都演示了如何创建一块buf,如何创建一块内存,并且得到他的地址。
关于ffi.string,这个模块是用于将内存块和lua的string做转换时使用。
为c类型绑定上元表
local ffi = require("ffi")
ffi.cdef[[
typedef struct { double x, y; } point_t;
]]
local point
local mt = {
__add = function(a, b) return point(a.x+b.x, a.y+b.y) end,
__len = function(a) return math.sqrt(a.x*a.x + a.y*a.y) end,
__index = {
area = function(a) return a.x*a.x + a.y*a.y end,
},
}
point = ffi.metatype("point_t", mt)
local a = point(3, 4)
print(a.x, a.y) --> 3 4
print(#a) --> 5
print(a:area()) --> 25
local b = a + point(0.5, 8)
print(#b) --> 12.5
这个就是在lua里面加载一个c语言的struct,并且在这个对象里面绑定上lua的实现方法。这个其实很奇妙的一个功能,今后如果一个c库,想做成面向对象就能通过这个调配一下就好了。
在官网中的解释
The C type metamethod mechanism is most useful when used in conjunction with C libraries that are written in an object-oriented style.