Lua C 交互

轉自:https://blog.csdn.net/yhhwatl/article/details/9303675

1. Lua的堆棧和全局表

我們來簡單解釋一下Lua的堆棧和全局表,堆棧大家應該會比較熟悉,它主要是用來讓C++和Lua通信的,是的,它們並不認識對方,只能通過堆棧來溝通,就像寫信一樣。

(旁白:它們不會用微信嗎?!微信~!不知道?)

 

 

Lua的全局表又是什麼呢?可以想象成是一個map哈希表結構,比如Lua有一個變量:

name = 「hello」

那麼,全局表就存放了」name」和」hello」的對應關係,Lua可以通過name在全局表中查找到hello。應該是這樣的~

(旁白:應該= =!)

 

 

2. Lua和C++的第一次通信

現在來設計一個場景,C++在一次JavaScript開發者大會上看到Lua在演講,於是C++被Lua深深吸引了。

(旁白:JavaScript大會...那爲毛是Lua在演講~!)

 

 

正文:

 

 

在這裏我僅簡單解釋一下Lua堆棧的索引,因爲我們在很多操作裏都涉及到堆棧的索引,比如上一章中我們要從堆棧中取得一個字符串,就必須給出堆棧索引:

[cpp] view plaincopy

  1. /* 獲取棧頂的值 */  
  2.     const char* str = lua_tostring(pL, 1);  
 
  1. /* 獲取棧頂的值 */

  2. const char* str = lua_tostring(pL, 1);


 

如果對堆棧索引不清晰的話,將會很糾結。

 

 

《遊戲人工智能編程案例精粹》一書的200頁,有一張圖,很好地表達了Lua的堆棧索引是如何定義的,我照着畫了一張:

(旁白:好醜~!而且還打了一個廣告,別以爲我不知道~!)

 

 

我們很明顯的看到堆棧的索引方式有兩種,一種是正數索引,一種是負數索引。

並且咋一看,好像兩種索引方式的規則是相反的,其實不然,我們來認真數數:

1. 正數索引,棧底是1,然後一直到棧頂是逐漸+1,最後變成9(9大於1)

2. 負數索引,棧底是-9,然後一直到棧頂是逐漸+1,最後變成-1(-1大於-9)

 

(旁白:這,這還真的是一樣的~!好神奇!)

 

 

對吧,一般像旁白那種人纔會認爲是相反的規則。

(旁白:吐槽是我的專利= =!)

 

 

大家不覺得奇怪嗎?爲什麼要用兩種方式?好混亂~!

我也覺得,但是有一點好處,看看它們各自的好處:

1. 正數索引,不需要知道棧的大小,我們就能知道棧底在哪,棧底的索引永遠是1

2. 負數索引,不需要知道棧的大小,我們就能知道棧頂在哪,棧頂的索引永遠是-1

(旁白:又好像有那麼一點道理。。。)

 

 

 

 

1. 什麼是table

table是Lua裏最強大的數據類型,我們可以當成是數組,但是它又和數組有點不一樣,建議大家看看Lua的語法教程,因爲我對table也沒有熟悉到可以給大家解釋的程度。

(旁白:那你還寫什麼教程。。。)

 

 

2. 獲取table變量

現在,我們給helloLua.lua文件添加一個table全局變量:

[cpp] view plaincopy

  1. -- helloLua.lua文件  
  2. myName = "beauty girl"  
  3.   
  4. helloTable = {name = "mutou", IQ = 125}  
 
  1. -- helloLua.lua文件

  2. myName = "beauty girl"

  3.  
  4. helloTable = {name = "mutou", IQ = 125}


 

我們看到,多了一個helloTable的變量,它和數組十分相似,又和HashMap有點類似,總之它很強大。

(旁白:我覺得亮點是,你的IQ有125?我覺得乘以2的話,還有點可能~!)

 

話說,125乘以2等於多少?...250 ....O O!

 

獲取helloTable變量的方式和以前是一樣的:

[cpp] view plaincopy

  1. /* 取得table變量,在棧頂 */  
  2.     lua_getglobal(pL, "helloTable");  
 
  1. /* 取得table變量,在棧頂 */

  2. lua_getglobal(pL, "helloTable");


 

這樣,helloTable變量就被存放到棧頂。

 

可我們並不是要取table變量,因爲C++中是無法識別Lua的table類型的,所以我們要取得table中具體的值,也就是name和IQ的值。

 

 

3. lua_gettable函數

有一個和lua_getglobal類似的函數,叫做lua_gettable,顧名思義,它是用來取得table相關的數據的。

(旁白:廢話少點好吧= =)

 

 

lua_gettable函數會從棧頂取得一個值,然後根據這個值去table中尋找對應的值,最後把找到的值放到棧頂。

lua_pushstring()函數可以把C++中的字符串存放到Lua的棧裏;

然後再用lua_gettable()取執行前面所說的步驟,lua_gettable的第二個參數是指定的table變量在棧中的索引。

(旁白:小笨木,我被你繞暈了。。。)

 

 

爲了照顧旁白這個笨蛋,我們畫個圖來理解:

這是初始狀態,堆棧裏還沒有任何東西,那麼,現在要先把helloTable變量放到棧頂:

[cpp] view plaincopy

  1. /* 取得table變量,在棧頂 */  
  2.     lua_getglobal(pL, "helloTable");  
 
  1. /* 取得table變量,在棧頂 */

  2. lua_getglobal(pL, "helloTable");


 

然後就變成了這樣:

接着,我們要取得table的name對應的值,那麼,先要做的就是把」name」字符串入棧:

[cpp] view plaincopy

  1. /* 將C++的字符串放到Lua的棧中,此時,棧頂變爲「name」, helloTable對象變爲棧底 */  
  2.     lua_pushstring(pL, "name");  
 
  1. /* 將C++的字符串放到Lua的棧中,此時,棧頂變爲「name」, helloTable對象變爲棧底 */

  2. lua_pushstring(pL, "name");


 

然後變成這樣:

(旁白:不帶這樣啊,你偷偷加上了棧的索引~!)

 

 

注意了,我把棧的索引也加上了,因爲我們即將要使用,這次我們用負數索引(不瞭解負數的索引的朋友請閱讀第03章的教程哈~)。

由於」name」的入棧,現在helloTable變量已經不在棧頂了。

 

接着,我們調用要做最重要的一步了,取得name在table中對應的值:

[cpp] view plaincopy

  1. /*  
  2.         從table對象尋找「name」對應的值(table對象現在在索引爲-2的棧中,也就是當前的棧底), 
  3.         取得對應值之後,將值放回棧頂 
  4.     */  
  5.     lua_gettable(pL, -2);  
 
  1. /*

  2. 從table對象尋找「name」對應的值(table對象現在在索引爲-2的棧中,也就是當前的棧底),

  3. 取得對應值之後,將值放回棧頂

  4. */

  5. lua_gettable(pL, -2);


 

此時,棧變成這樣:

(旁白:發生什麼事?爲什麼「mutou」突然出現在棧頂?!爲毛!是你自己畫上去的吧!)

 

lua_gettable倒底做了什麼事情?

首先,我們來解釋一下lua_gettable的第二個參數,-2是什麼意思,-2就是剛剛helloTable變量在棧中的索引。

然後,Lua會去取得棧頂的值(之前的棧頂是」name」),然後拿着這個值去helloTable變量中尋找對應的值,當然就找到」mutou」了。

最後,Lua會把找到的值入棧,於是」mutou」就到了棧頂了。

 

(旁白:你妹紙的。。。沒事,我就罵罵人)

 

 

最後我們只需要取出棧頂的數據就可以了,完整代碼如下:

[cpp] view plaincopy

  1. /* 初始化 */  
  2.     lua_State* pL = lua_open();  
  3.     luaopen_base(pL);  
  4.   
  5.     /* 執行腳本 */  
  6.     luaL_dofile(pL, "helloLua.lua");  
  7.   
  8.     /* 取得table變量,在棧頂 */  
  9.     lua_getglobal(pL, "helloTable");  
  10.   
  11.     /* 將C++的字符串放到Lua的棧中,此時,棧頂變爲「name」, helloTable對象變爲棧底 */  
  12.     lua_pushstring(pL, "name");  
  13.   
  14.     /*  
  15.         從table對象尋找「name」對應的值(table對象現在在索引爲-2的棧中,也就是當前的棧底), 
  16.         取得對應值之後,將值放回棧頂 
  17.     */  
  18.     lua_gettable(pL, -2);  
  19.   
  20.     /* 現在表的name對應的值已經在棧頂了,直接取出即可 */  
  21.     const char* sName = lua_tostring(pL, -1);  
  22.     CCLOG("name = %s", sName);  
 
  1. /* 初始化 */

  2. lua_State* pL = lua_open();

  3. luaopen_base(pL);

  4.  
  5. /* 執行腳本 */

  6. luaL_dofile(pL, "helloLua.lua");

  7.  
  8. /* 取得table變量,在棧頂 */

  9. lua_getglobal(pL, "helloTable");

  10.  
  11. /* 將C++的字符串放到Lua的棧中,此時,棧頂變爲「name」, helloTable對象變爲棧底 */

  12. lua_pushstring(pL, "name");

  13.  
  14. /*

  15. 從table對象尋找「name」對應的值(table對象現在在索引爲-2的棧中,也就是當前的棧底),

  16. 取得對應值之後,將值放回棧頂

  17. */

  18. lua_gettable(pL, -2);

  19.  
  20. /* 現在表的name對應的值已經在棧頂了,直接取出即可 */

  21. const char* sName = lua_tostring(pL, -1);

  22. CCLOG("name = %s", sName);