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庫,就可以調用注冊的函數了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM