lua与c之间交互详解(三)
本篇主要讲解Lua是如何调用c的,Lua是宿主语言,c是附加语言,关于c如何调用Lua参考其他两篇。Lua调用c有几种不同方式,这里只讲解最常用的一种:将c模块编译成so库,然后供Lua调用。
gcc mylib.c -fPIC -shared -o mylib.so -I/usr/local/include/
约定:c模块需提供luaopen_xxx接口,xxx与文件名必须一致,比如"mylib";还需提供一个注册数组(55-60行),该数组必须命名为luaL_Reg,每一项是{lua函数名,c函数名},最后一项是{NULL, NULL};通过luaL_newlib创建新的表入栈,然后将数组中的函数注册进去,这样Lua就可以调用到。
1 //mylib.c 2 3 #include <lua.h> 4 #include <lauxlib.h> 5 #include <lualib.h> 6 7 #define TYPE_BOOLEAN 1 8 #define TYPE_NUMBER 2 9 #define TYPE_STRING 3 10 11 static int ladd(lua_State *L){ 12 double op1 = luaL_checknumber(L, -2); 13 double op2 = luaL_checknumber(L, -1); 14 lua_pushnumber(L, op1+op2); 15 return 1; 16 } 17 18 static int lsub(lua_State *L){ 19 double op1 = luaL_checknumber(L, -2); 20 double op2 = luaL_checknumber(L, -1); 21 lua_pushnumber(L, op1-op2); 22 return 1; 23 } 24 25 static int lavg(lua_State *L){ 26 double avg = 0.0; 27 int n = lua_gettop(L); 28 if(n==0){ 29 lua_pushnumber(L,0); 30 return 1; 31 } 32 int i; 33 for(i=1;i<=n;i++){ 34 avg += luaL_checknumber(L, i); 35 } 36 avg = avg/n; 37 lua_pushnumber(L,avg); 38 return 1; 39 } 40 41 static int fn(lua_State *L){ 42 int type = lua_type(L, -1); 43 printf("type = %d\n", type); 44 if(type==LUA_TBOOLEAN){ 45 lua_pushvalue(L, lua_upvalueindex(TYPE_BOOLEAN)); 46 } else if(type==LUA_TNUMBER){ 47 lua_pushvalue(L, lua_upvalueindex(TYPE_NUMBER)); 48 } else if(type==LUA_TSTRING){ 49 lua_pushvalue(L, lua_upvalueindex(TYPE_STRING)); 50 } 51 return 1; 52 } 53 54 int luaopen_mylib(lua_State *L){ 55 luaL_Reg l[] = { 56 {"add", ladd}, 57 {"sub", lsub}, 58 {"avg", lavg}, 59 {NULL, NULL}, 60 }; 61 luaL_newlib(L,l); 62 63 lua_pushliteral(L, "BOOLEAN"); 64 lua_pushliteral(L, "NUMBER"); 65 lua_pushliteral(L, "STRING"); 66 lua_pushcclosure(L, fn, 3); 67 68 lua_setfield(L, -2, "fn"); 69 return 1; 70 }
Lua文件里,需将so库加入cpath路径里,通过require返回栈上的表,Lua就可以调用表中注册的接口,比如,add、sub、avg等
Lua调用c api的过程:Lua将api需要的参数入栈——c提取到参数——处理——c将结果入栈——Lua提取出结果
1 package.cpath = "./?.so" 2 3 local mylib = require "mylib" 4 5 local a, b = 3.14, 1.57 6 7 print(mylib.add(a, b), mylib.sub(a, b)) -- 4.71. 1.57 8 9 print(mylib.avg()) -- 0.0 10 11 print(mylib.avg(1,2,3,4,5)) -- 3.0 12 13 print(mylib.fn(true), mylib.fn(10), mylib.fn("abc")) -- BOOLEAN NUMBER STRING
示例中还提供了简单的c闭包的使用方法(63-68行),关于c闭包,提供了多个上值(upvalue)关联到函数上,这些upvalue可以理解成该函数内部的全局变量,即只能被该函数访问到,且在函数返回时不会消亡,该函数任何时候都可以访问到。
void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);
用来把一个新的c闭包压栈,fn是一个c api,n指定关联多少个upvalue,这些upvalue需要依次压栈,即栈顶位置是第n个upvalue的值,lua_pushcclosure会把这些upvalue出栈,这些upvalue的伪索引依次为1-n。
int lua_upvalueindex (int i);
获取当前运行函数第i个upvalue的值。
总之,Lua调用c的流程:编写好c模块,在堆栈上建一个表,将接口注册给这个表,然后把c模块编译成so库,在Lua里require这个so库,就可以调用注册的函数了。