lua5.3調用C/C++


馬上面臨畢業設計,打算做點跟網游有關的,先從做周邊工具開始,目前正在做一個協議序列化和反序列化的東西,廣告一波先: https://github.com/Anti-Magic/rproto

目前非常簡陋,功能還沒做完,不要當真。。

因為目標是綁定到lua,作為一個獨立的庫,不想對項目有依賴,這樣的好處是客戶端和服務端都可以方便的拿來用,所以打算手動綁定。

 

我夜觀天象發現,到目前為止網上找不到針對lua5.3的這么簡明扼要且完整的示例,轉載注明出處:http://www.cnblogs.com/wolfred7464/p/5147675.html

 

由於lua提供的require函數可以引入C語言的動態鏈接庫,對於使用者來說,不需配置,只要調用require就可以,肯定是最簡單的方法。(其實iOS不能這樣做的,唉,習慣就好)

首先讀者需要對lua提供的操作虛擬棧的函數比較熟悉,不熟悉的先翻手冊吧:http://cloudwu.github.io/lua53doc/manual.html

先上一段簡單的示例,功能是使用C語言編寫一個計算2個浮點數之和的add函數,綁定到lua由lua調用:

 1 #include "lua.h"
 2 #include "lualib.h"
 3 #include "lauxlib.h"
 4 #include "luaconf.h"
 5 
 6 double add(double x, double y) {
 7     return x+y;
 8 }
 9 
10 static int ladd(lua_State* L) {
11     double x = luaL_checknumber(L, 1);
12     double y = luaL_checknumber(L, 2);
13     lua_pushnumber(L, add(x+y));
14     return 1;
15 }
16 
17 int luaopen_xxx(lua_State* L) {
18     luaL_checkversion(L);
19 
20     struct luaL_Reg funcs[] = {
21         {"add", ladd},
22         {NULL,  NULL}
23     };
24     luaL_newlib(L, funcs);
25     return 1;
26 }
View Code

在linux使用gcc編譯:

gcc main.c -shared -fPIC -I/usr/local/include -o xxx.so

lua的package.cpath中需要能搜索到我們的so,設置cpath的方法網上搜一下有很多。然后lua代碼中這樣調用:

1 local xxx = require "xxx"
2 print(xxx.add(45, 5))
View Code

最后在終端執行:

lua test.lua

這個例子就跑起來了,雖然步驟說不上多簡單,但是足夠清晰吧。。為了簡單,我就不寫檢查參數數量和類型是否匹配的代碼了,后面的例子也是。

 

如何使用lua調用C++呢?只要你會操作userdata,基本上照搬上面的C語言的方式就可以,只不過顯式傳遞C++的this指針就可以。再上一個簡單的代碼:

 1 #include <lua.hpp>
 2 
 3 class TTT {
 4 public:
 5     TTT() {}
 6     ~TTT() {}
 7 
 8     double add(double x, double y) {
 9         return x+y;
10     }
11 };
12 
13 extern "C"
14 {
15     static int lnewTTT(lua_State* L) {
16         TTT** p = (TTT**)lua_newuserdata(L, sizeof(TTT*));
17         *p = new TTT();
18         return 1;
19     }
20 
21     static int ldelTTT(lua_State* L) {
22         TTT** p = (TTT**)lua_topointer(L, 1);
23         delete *p;
24         *p = nullptr;
25         return 0;
26     }
27 
28     static int ladd(lua_State* L) {
29         TTT** p = (TTT**)lua_topointer(L, 1);
30         TTT* pt = *p;
31         lua_pushnumber(L, pt->add(lua_tonumber(L, 2), lua_tonumber(L, 3)));
32         return 1;
33     }
34 
35     int luaopen_xxx(lua_State* L) {
36         luaL_checkversion(L);
37         luaL_openlibs(L);
38 
39         struct luaL_Reg funcs[] = {
40             {"new", lnewTTT},
41             {"add", ladd},
42             {"del", ldelTTT},
43             {NULL,  NULL}
44         };
45         luaL_newlib(L, funcs);
46         return 1;
47     }
48 }
View Code
1 local xxx = require "xxx"
2 local t = xxx.new()
3 print(xxx.add(t, 3, 4))
4 xxx.del(t)
View Code

(為什么要使用指針的指針呢?因為lua申請內存和釋放內存時不會調用C++的構造函數和析構函數,我們又不能手動去調用,只能使用指針的指針,便於手動調用構造和析構函數了。)

使用lua的userdata保存this指針,然后作為參數傳遞到綁定函數,然后C語言就可以調用了,第一個例子明白后,這個沒什么難度。

但是這樣做有三個問題:一是無法檢查lua傳來的this指針是否是正確的類型,二是需要手動釋放內存,三是lua代碼中調用起來太麻煩。

這三個問題有一個共同的解決方案:元表(metatable)。

1、給同一類userdata設置同一個元表,利用元表就可以區別不同類型的userdata。

2、lua垃圾回收時會調用元表中的"__gc"元方法,我們可以利用這個元方法析構C++對象。

3、索引字段會調用"__index"方法,不存在這個方法時會從元表中查找,利用這個特性我們可以把red.add(t, x, y)這樣的調用變成t.add(t, x, y),然后就可以寫成t:add(x, y)了。

把第二個例子中的lnewTTT和luaopen_xxx兩個函數修改一下就可以,上代碼:

 1 static int lnewTTT(lua_State* L) {
 2     TTT** p = (TTT**)lua_newuserdata(L, sizeof(TTT*));
 3     *p = new TTT();
 4     luaL_getmetatable(L, "xxx.TTT");
 5     lua_setmetatable(L, -2);
 6     return 1;
 7 }
 8 
 9 int luaopen_xxx(lua_State* L) {
10     luaL_checkversion(L);
11     luaL_openlibs(L);
12 
13     struct luaL_Reg funcs[] = {
14         {"new", lnewTTT},
15         {NULL,  NULL}
16     };
17 
18     struct luaL_Reg funcs_meta[] = {
19         {"add", ladd},
20         {NULL,  NULL}
21     };
22 
23     luaL_newmetatable(L, "xxx.TTT");
24     lua_pushstring(L, "__gc");
25     lua_pushcfunction(L, ldelTTT);
26     lua_settable(L, -3);
27     lua_pushstring(L, "__index");
28     lua_pushvalue(L, -2);
29     lua_settable(L, -3);
30     luaL_setfuncs(L, funcs_meta, 0);
31     luaL_newlib(L, funcs);
32     return 1;
33 }
View Code
1 local xxx = require "xxx"
2 local t = xxx.new()
3 print(t:add(3, 4))
View Code

基本上就是這樣了,綁定其他的數據類型,或者保存上下文等,都可以查找手冊解決,lua天生為了嵌入宿主語言而設計,與C語言的交互有天然的優勢。

 


免責聲明!

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



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