lua對象調用—用 "." 與 ":" 調用表中函數時的區別


lua對象調用—用 "." 與 ":" 調用表中函數時的區別

寫這篇帖子之前,我看過許多關於綁定C++對象到Lua中的文章。總結一下他們的做法,用到元表、注冊表、以及一些表中函數操作的一些基礎知識以及相關的細節

相信大家對Lua的表一點也不陌生,Lua表是個神奇的東西(本質上就是hash表),可以當做數組,可以當做map,還可用來模擬面向對象,這很Lua。
我們在Lua中模擬面向對象的步驟中大家有沒有仔細去研究過用 "." 與 ":" 調用表中函數時的區別呢,下面帶大家一起來研究一下。

例1

[Lua] 例1
 1 --code 1:
 2 --定義一張表obj
 3 obj = {}
 4 --下面的這行代碼等價於obj["func"]=function(a,b,c)...
 5 obj.func = function(a, b, c)
 6     print(a, b, c)
 7 end
 8  
 9 print("obj:", obj)
10 obj.func(1,2)
11 obj:func(1,2)

 輸出:

[Shell]

obj: obj表地址
1   2    nil
obj表地址    1    2

 

從上面的輸出可以看出,當我們可以發現,當用"."來調用表內的函數時,與普通的函數調用沒啥區別。

但是當我們用":"來調用時,會把調用這個函數的那張表的傳進去第一個參數中,后面的參數與實參一樣。
有了這個發現我們就可以繼續往下探究。
但是有一種情況是不會傳入表到第一個參數的。看如下代碼

例2

[Lua] 例2
1 --下面這種定義表中函數的當大相信大家都不陌生
2 obj = {}
3 function obj:func(a, b, c)
4     print(a, b, c)
5 end
6  
7 print("obj:", obj)
8 obj.func(1, 2)
9 obj:func(1, 2)

輸出:

[Shell]
obj:obj表地址
2    nil    nil
1    2    nil

 

發現了吧,當我們用如"function 表:函數名(......) .... end"的形式去定義一個表中的函數時,如果我們用"."來調用這個函數,就會得到莫名其妙的東西,這個現象不再我們的討論之內;對於上面的定義方式,如果是用":"來調用那么跟調用普通的函數沒啥區別。

這個了解一下就好,因為我們用lua c api 操作表中函數時用的是上上面的那種定義形式。

那么如果把這個obj表作為一個元表__index鍵的值再把這個元表與其他的表進行綁定,在觸發__index時傳的是哪張表進第一的參數呢
有如下代碼:

例3

[Lua] 例3
 1 obj = {}
 2 obj.func = function(a, b, c)
 3     print(a, b, c)
 4 end
 5  
 6 mtable = {__index = obj}
 7 otherobj = {}
 8  
 9 setmetatable(otherobj, mtable)
10  
11 print("obj:", obj)
12 print("mtable", mtable)
13 print("otherobj:", otherobj)
14  
15 otherobj.func(1, 2)
16 otherobj:func(1, 2)

輸出:

[Shell]
obj:  obj表地址
mtable:  mtable表地址
otherobj:  otherobj表地址
1    2    nil
otherobj表地址    1    2

 

從上面的結果可以知道,就算是在觸發__index元方法調用的函數,如果是用":"來調用,那么一樣會傳入調用這個函數的那張表進第一個參數,就算是嵌套多層元表也是如此,大家感興趣可以試一試嵌套多層元表,結果是一樣的。

元表還有一些小知識,就是__index鍵對應的是一個函數的時候也會把調用這個函數的表傳進去,然后第二個參數是要調用的那個函數的名字,這時你就得手動處理是返回一個函數還是返回一個其他的量了。這個大家感興趣也可以做做實驗,這也是元表的基礎知識。

那么,如果這個函數時我們自己寫的CFunction呢,還能這么如願么,看下面的代碼:

例4

[C] 例4
 1 //filename:funct.c
 2  
 3 #define LUA_LIB
 4 #include <lua.h>
 5 #include <lualib.h>
 6 #include <lauxlib.h>
 7  
 8 static int _c_l_testfunc(lua_State* L)
 9 {
10     unsigned char argc, index;
11     const char *typename;
12     if ((argc = lua_gettop(L)) != 0) {
13         printf("共傳入 %d 個參數\n", argc);
14         for (index = 1; index <= argc; index++) {
15             printf(
16                 "第 %d 個參數類型為: %s\n",
17                 index, lua_typename(L, lua_type(L, index))
18             );
19         }
20     } else {
21         puts("0 個參數傳入");
22     }
23      
24     //清空棧
25     lua_settop(L, 0);
26      
27     //把參數個數壓入棧作為返回值
28     lua_pushinteger(L, argc);
29      
30     return 1;
31 }
32  
33 #if define(__cplusplus)
34 #define EXP_FUNC __declspec(dllexport)
35 #else
36 #define EXP_FUNC
37 #endif
38  
39  
40 LUA_API EXP_FUNC int luaopen_funct(lua_State* L)
41 {
42     lua_pushcfunction(L, _c_l_testfunc);
43     return 1;
44 }

 

上面的這個函數又來輸出調用這個函數時的參數以及參數的類型。
把上面的代碼編譯成動態庫,我們來在lua中調用這個函數

例5

[lua] 例5
 1 funct = require "funct"
 2  
 3 --建一個表來存這個函數
 4 obj = {}
 5 obj.func = funct
 6  
 7 print("\".\"調用:")
 8 obj.func(1, "str")
 9 print("\":\"調用:")
10 obj:func(1, "str")

輸出:

[Shell]
"."調用:
共傳入 2 個參數
第 1 個參數類型為: number
第 2 個參數類型為: string
":"調用
共傳入 3 個參數
第 1 個參數類型為: table
第 2 個參數類型為: number
第 3 個參數類型為: string

 

發現了吧,與lua內函數定義的函數傳參沒區別。如果大家半信半疑的話再把剛才那個C代碼改一下,再添加一個把某個參數位置的值輸出出來。這跑一下這個例子就明白了。現在告訴大家的是就算是CFunction,我們使用":"調用時也會把調用這個函數的表傳進第一個參數,這使得我么把C++綁進lua中有方法可尋。


還有一點,就是我們的userdata可以與元表綁定,lightuserdata不可以與元表綁定。如果我們在Lua中是對一個userdata進行":"調用的話,那么就會觸發與之綁定的元表的__index方法,而且這回傳進去的不是表了,是這個userdata,我們用lua_touserdata轉換得到相應的對象指針。

在Lua中,我們不能直接對userdata進行操作,但是我們可以間接的通過與之綁定的元表的__index進行操作。
有一個點我們得注意的是,CFunction是普通成員函數指針類型,與類成員函數指針不一樣,我們得再封裝一層函數,這樣子才能把函數綁定在我們的函數表中。
大致思路:
1.與類成員函數同名的CFunction,並且在其中完成對相應類成員函數的調用。
2.把定義的CFunction打包到一張函數表methods中
3.創建元表,設置鍵值對:__index = methods
4.把這張元表綁定到userdata上(我們的對象指針)
5.垃圾回收(主動回收和__gc被動回收)

 

 

 

參考:https://www.52pojie.cn/thread-833988-1-1.html
參考:解決C++成員變量在lua中直接使用的問題https://www.cnblogs.com/liao0001/p/9791557.html


免責聲明!

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



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