Lua FFI 您所在的位置:网站首页 fputs返回值 Lua FFI

Lua FFI

#Lua FFI | 来源: 网络整理| 查看: 265

简介

对 libffi 的介绍可以看 [这里],简单来说它就是提供了动态调用任意 C 函数的功能。

FFI库允许调用外部C函数并使用来自纯Lua代码的C数据结构。 FFI库基本上避免了在C中编写繁琐的手动Lua / C绑定的需要。

libffi主要的功能包括两个:

动态调用动态定义动态调用,在运行时动态调用一个函数。例1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include #include int main() { ffi_cif cif; ffi_type *args[1]; void *values[1]; char *s; ffi_arg rc; /* Initialize the argument info vectors */ /* 初始化参数信息 */ args[0] = &ffi_type_pointer; values[0] = &s; /* Initialize the cif */ /* 初始化cif */ if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1, &ffi_type_sint, args) == FFI_OK) { s = "Hello World!"; ffi_call(&cif, puts, &rc, values); /* rc now holds the result of the call to puts */ /* values holds a pointer to the function's arg, so to call puts() again all we need to do is change the value of s */ s = "This is cool!"; ffi_call(&cif, puts, &rc, values); } return 0; } 例2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 int testFunc(int m, int n) { printf("params: %d %d \n", n, m); return n+m; } int main() { //拿函数指针 void* functionPtr = dlsym(RTLD_DEFAULT, "testFunc"); int argCount = 2; //按ffi要求组装好参数类型数组 ffi_type **ffiArgTypes = alloca(sizeof(ffi_type *) *argCount); ffiArgTypes[0] = &ffi_type_sint; ffiArgTypes[1] = &ffi_type_sint; //按ffi要求组装好参数数据数组 void **ffiArgs = alloca(sizeof(void *) *argCount); void *ffiArgPtr = alloca(ffiArgTypes[0]->size); int *argPtr = ffiArgPtr; *argPtr = 1; ffiArgs[0] = ffiArgPtr; void *ffiArgPtr2 = alloca(ffiArgTypes[1]->size); int *argPtr2 = ffiArgPtr2; *argPtr2 = 2; ffiArgs[1] = ffiArgPtr2; //生成 ffi_cfi 对象,保存函数参数个数/类型等信息,相当于一个函数原型 ffi_cif cif; ffi_type *returnFfiType = &ffi_type_sint; ffi_status ffiPrepStatus = ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, (unsigned int)0, (unsigned int)argCount, returnFfiType, ffiArgTypes); if (ffiPrepStatus == FFI_OK) { //生成用于保存返回值的内存 void *returnPtr = NULL; if (returnFfiType->size) { returnPtr = alloca(returnFfiType->size); } //根据cif函数原型,函数指针,返回值内存指针,函数参数数据调用这个函数 ffi_call(&cif, functionPtr, returnPtr, ffiArgs); //拿到返回值 int returnValue = *(int *)returnPtr; printf("ret: %d \n", returnValue); } }

上面代码展示,只需要知道

函数指针(puts)包括参数和返回值信息的函数原型(cif)调用参数(values)保存返回值的指针(rc)

就可以在运行时动态地调用一个函数。

动态定义,运行时增加新的函数。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include #include /* Acts like puts with the file given at time of enclosure. */ /* 新的函数实体 */ void puts_binding(ffi_cif *cif, void *ret, void* args[], void *stream) { *(ffi_arg *)ret = fputs(*(char **)args[0], (FILE *)stream); } /* 函数原型 */ typedef int (*puts_t)(char *); int main() { ffi_cif cif; ffi_type *args[1]; ffi_closure *closure; void *bound_puts; int rc; /* Allocate closure and bound_puts */ /* 给闭包和bound_puts分配内存 */ closure = ffi_closure_alloc(sizeof(ffi_closure), &bound_puts); if (closure) { /* Initialize the argument info vectors */ /* 初始化参数信息 */ args[0] = &ffi_type_pointer; /* Initialize the cif */ /* 初始化cif */ if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1, &ffi_type_sint, args) == FFI_OK) { /* Initialize the closure, setting stream to stdout */ /* 初始化闭包,设置stream参数为stdout */ if (ffi_prep_closure_loc(closure, &cif, puts_binding, stdout, bound_puts) == FFI_OK) { rc = ((puts_t)bound_puts)("Hello World!"); /* rc now holds the result of the call to fputs */ /* rc 保存了fputs的返回值 */ } } } /* Deallocate both closure, and bound_puts */ /* 释放闭包和bound_puts函数指针 */ ffi_closure_free(closure); return 0; }

这个例子,动态定义了一个bound_puts函数指针,并绑定到puts_binding函数实体上。

动态定义时使用的api是ffi_prep_closure_loc函数,需要准备好closure,函数原型(cif),函数实体(puts_binding),透传的userInfo(stdout),函数指针(bound_puts)

closure是ffi_closure类型对象,用于把其它各个参数关联到一起。

入门 “Hello world”调用外部C函数 1 2 3 4 5 local ffi = require("ffi") ffi.cdef[[ int printf(const char *fmt, ...); ]] ffi.C.printf("Hello %s!\n", "world") 使用C数据结构

FFI库允许创建和访问C数据结构

简单Lua版本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 --cdata_plain.lua local floor = math.floor local function image_ramp_green(n) local img = {} local f = 255/(n-1) for i=1,n do img[i] = { red = 0, green = floor((i-1)*f), blue = 0, alpha = 255 } end return img end local function image_to_grey(img, n) for i=1,n do local y = floor(0.3*img[i].red + 0.59*img[i].green + 0.11*img[i].blue) img[i].red = y; img[i].green = y; img[i].blue = y end end local N = 400*400 local img = image_ramp_green(N) for i=1,1000 do image_to_grey(img, N) end

运行

1 2 3 4 5 time ./luajit cdata_plain.lua real 0m4.570s user 0m4.547s sys 0m0.020s FFI版本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 --cdata_ffi.lua local ffi = require("ffi") ffi.cdef[[ typedef struct { uint8_t red, green, blue, alpha; } rgba_pixel; ]] local function image_ramp_green(n) local img = ffi.new("rgba_pixel[?]", n) local f = 255/(n-1) for i=0,n-1 do img[i].green = i*f img[i].alpha = 255 end return img end local function image_to_grey(img, n) for i=0,n-1 do local y = 0.3*img[i].red + 0.59*img[i].green + 0.11*img[i].blue img[i].red = y; img[i].green = y; img[i].blue = y end end local N = 400*400 local img = image_ramp_green(N) for i=1,1000 do image_to_grey(img, N) end

运行

1 2 3 4 5 time ./luajit cdata_ffi.lua real 0m0.576s user 0m0.575s sys 0m0.001s 性能对比 1 4.570/0.576 = 7.93403

FFI版本的耗时只有简单Lua版本的 1/8 。

进阶 常用的操作IdiomC codeLua codePointer dereferencex = *p;x = p[0]int *p;*p = y;p[0] = yPointer indexingx = p[i];x = p[i]int i, *p;p[i+1] = y;p[i+1] = yArray indexingx = a[i];x = a[i]int i, a[];a[i+1] = y;a[i+1] = ystruct/union dereferencex = s.field;x = s.fieldstruct foo s;s.field = y;s.field = ystruct/union pointer deref.x = sp->field;x = s.fieldstruct foo *sp;sp->field = y;s.field = yPointer arithmeticx = p + i;x = p + iint i, *p;y = p - i;y = p - iPointer differenceint p1, p2;x = p1 - p2;x = p1 - p2Array element pointerint i, a[];x = &a[i];x = a+iCast pointer to addressint *p;x = (intptr_t)p;x = tonumber(ffi.cast(“intptr_t”,p))Functions with outargsvoid foo(int *inoutlen);int len = x;foo(&len);y = len;local len =ffi.new(“int[1]”, x)foo(len)y = len[0]Vararg conversionsint printf(char *fmt, …);printf(“%g”, 1.0);printf(“%d”, 1);printf(“%g”, 1);printf(“%d”,ffi.new(“int”, 1))缓存或者不缓存普通Lua 1 2 3 4 local byte, char = string.byte, string.char local function foo(x) return char(byte(x)+1) end

这将用(更快)直接使用 localvalue 或 upvalue 来替换几个哈希表查找。

FFI

这种情况与通过FFI库的C函数调用有点不同。缓存单个C函数没有帮助,实际上是反效果

1 2 3 4 local funca, funcb = ffi.C.funca, ffi.C.funcb -- Not helpful! local function foo(x, n) for i=1,n do funcb(funca(x, i), 1) end end

应该缓存命名空间

1 2 3 4 local C = ffi.C -- Instead use this! local function foo(x, n) for i=1,n do C.funcb(C.funca(x, i), 1) end end


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有