如何在Lua与C/C++之间实现table数据的交换
為什么80%的碼農都做不了架構師?>>>
? ??之前在《C/C++和Lua是如何進行通信的?》一文中簡單的介紹了lua與宿主之間的通信。簡單的說兩種不同的語言之間數據類型不一樣又如何進行數據交換呢?那就是lua_State虛擬棧,通過棧操作和lua庫函數,我們很輕松就能完成兩者之間的數據交換。
????開始之前,明確幾個問題,lua中的虛擬棧的索引編號問題(我們假設棧大小為n),編號1是棧底,n視棧頂,編號-1是棧頂,-n是棧底。lua中的庫函數需要訪問和操作棧上的數據都是通過索引編號定位的。但是我們需要明確一點,有些API并沒有使用索引編號作為參數,意味著默認對棧頂進行操作。如lua_pushnumber(L, 66)將數值66壓入棧頂,lua_tonumber(L, -1)取編號-1(棧頂)元素等等,如果這些基本知識和API的都已經熟悉了,那么lua與宿主之間的數據交換就很容易理解了。
????姿勢準備好了,那么問題來了。需求:我們現在要設計一個UI界面,我們希望這個UI是可以重用的。為了滿足這個需求,顯然我們必須將UI界面與顯示數據分離。使用lua初始化數據后,將數據傳遞給UI界面然后顯示。這樣如果需求變更(游戲開發中經常產生這樣的需求),我們也只需改變lua腳本就能重用UI界面,聽上去真是程序猿的福音啊~~
????用于顯示UI的數據必定很多,需要使用lua中的table來封裝這些數據,現在給定如下lua table數據:
我們的腳本將調用一個程序封裝好的c API(TestTable函數),然后將tTest作為參數,壓入虛擬棧中,如下:
local?tRet?=?TestTable(tTest);雖然tTest table已經傳給了程序,我們還需要對TestTable這個c API進行定制,使它能夠正確的理解這個table中的數據,實現代碼如下(LuaTestTable函數類型是lua_CFuntion類型,注冊到lua虛擬機中的函數名為TestTable):
int?LuaTestTable(lua_State*?L) {printf("stack?size?=?%d\n",?lua_gettop(L));?//打印棧中元素的個數lua_pushstring(L,?"gdp");????????????//將gdp字符串壓入棧頂//根據棧頂的key獲取table中的value,將key(這里的“gdp”)移除,再將value壓入棧頂lua_gettable(L,?1);??????????????????????printf("%s\n",?lua_tostring(L,?-1));?//取棧頂元素(注意這里的整型值都是string類型)lua_pop(L,?1);?//取完之后清理棧頂printf("stack?size?=?%d\n",?lua_gettop(L));?//打印棧中元素的個數lua_pushstring(L,?"info");???//同上lua_gettable(L,?1);printf("%s\n",?lua_tostring(L,?-1));lua_pop(L,?1);printf("stack?size?=?%d\n",?lua_gettop(L));lua_pushstring(L,?"task");?//這里的value值是一個table哦,沒關系棧操作都是一樣的lua_gettable(L,?1);for?(int?i?=?0;?i?<?4;?++i){lua_pushnumber(L,?i+1);lua_gettable(L,?-2);printf("%s\n",?lua_tostring(L,?-1));lua_pop(L,?1);}lua_pop(L,?1);printf("stack?size?=?%d\n",?lua_gettop(L));?//到這里tTest表依然在棧底,但不影響后面的操作。//------華麗的分割線------------////到這里table數據的解析就結束了,以下內容是c?API給lua返回table數據lua_newtable(L);//要給lua腳本返回一個table類型,先要new一個,壓入棧頂lua_pushnumber(L,?1);?//將key先壓入棧lua_pushstring(L,?"table2lua");?//再將value壓入棧lua_settable(L,?-3);//settable將操作-2,-1編號的鍵值對,設置到table中,并把key-value從棧中移除lua_pushstring(L,?"key");?//同上lua_newtable(L);?//這里有個子tablelua_pushstring(L,?"capi");//這里的value類型使用lua_CFunction類型,可用做c?API調用,函數實現請參看附錄1lua_pushcfunction(L,?LuaSayHello);?lua_settable(L,?-3);lua_pushnumber(L,?2);lua_pushnumber(L,?10086);lua_settable(L,?-3);lua_settable(L,?-3);?//這個從這里“lua_pushstring(L,?"key");?//同上”開始匹配的printf("stack?size?=?%d\n",?lua_gettop(L));return?1;?//返回棧頂1個元素 }需要說明的是在lua中tTest["gdp"]和tTest.gdp的調用形式是一樣的,這是lua的語法糖。當然操作棧中的table方法除了lua_gettable和lua_settable還有其它方法,請參看lua_rawget和lua_rawset。理解棧中的元素變化是非常重要的。
????LuaTestTable函數API的后面部分介紹了構造一個任意table作為返回值,返回給lua腳本。首先使用lua_newtable庫函數新建一個table類型的數據,并壓入棧。然后將鍵值對key-value依次壓入棧,調用lua_settable(L, index)將key-value設置到table中,子table操作也是一樣的(這里的index指的是要設置的table在棧中的索引編號)。
????好了,基本介紹完了,最后來編寫腳本,看看效果(以下是程序調用的腳本):
TestTable成功解析tTest的數據,并且返回一個table類型的tRet。
printTable(tRet);printTable簡單的實現了一個table打印的腳本將tRet打印輸出。
腳本調用tRet中返回的c API類型的函數。具體實現請參見附錄。
運行結果:
stack?size?=?1??//以下是LuaTestTable的輸出 1234 stack?size?=?1 this?is?test?about?exchange?table?data! stack?size?=?1 12 23 34 45 stack?size?=?1??? stack?size?=?2???//以下是printTable的輸出 {[1]?=?table2lua[key]?=?{[2]?=?10086[capi]?=?function:?0x409795} } Lua?call?c/c++:SayHello()?//這里是?[capi]?=?function:?0x409795被調用的輸出 Hello?Everyone!綜上述,我們只需要修改tTest中的數據(結構不能改,改了需要修改LuaTestTable函數)就能改變UI界面的顯示,成功的解決了UI界面的復用問題。(文中沒有具體講到如何UI截面相關的細節,請參照例子自行腦補。)
附錄1:
//注冊到lua虛擬機中的c?API函數 int?LuaSayHello(lua_State*?L) {printf("Lua?call?c/c++:SayHello()\n");printf("Hello?Everyone!\n");return?0; }附錄2:
//腳本函數實現打印一個table function?printTable(t,?n)if?"table"?~=?type(t)?thenreturn?0;endn?=?n?or?0;local?str_space?=?"";for?i?=?1,?n?dostr_space?=?str_space.."??";endprint(str_space.."{");for?k,?v?in?pairs(t)?dolocal?str_k_v?=?str_space.."??["..tostring(k).."]?=?";if?"table"?==?type(v)?thenprint(str_k_v);printTable(v,?n?+?1);elsestr_k_v?=?str_k_v..tostring(v);print(str_k_v);endendprint(str_space.."}"); end完整項目托管在github上:?https://github.com/xlplbo/Lua2Game.git(支持vs2013和cmake編譯)
轉載請申明出處,如有任何疑問或建議指出,謝謝~
轉載于:https://my.oschina.net/xlplbo/blog/398792
總結
以上是生活随笔為你收集整理的如何在Lua与C/C++之间实现table数据的交换的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 辞职信格式模板和范文参考
- 下一篇: ST_LINK/V2 SWIM和SWD、