三C++調用Lua
我們經常可以使用Lua文件來作配置文件。類似ini,xml等文件配置信息。現在我們來使用C++來讀取Lua文件中的變量,table,函數。
lua和c通信時有這樣的約定: 所有的lua中的值由lua來管理, c++中產生的值lua不知道, 類似表達了這樣一種意思: "如果你(c/c++)想要什麼, 你告訴我(lua), 我來產生, 然後放到棧上, 你只能通過api來操作這個值, 我只管我的世界", 這個很重要, 因為:
"如果你想要什麼, 你告訴我, 我來產生"就可以保證, 凡是lua中的變量, lua要負責這些變量的生命周期和垃圾回收, 所以, 必須由lua來創建這些值(在創建時就加入了生命周期管理要用到的簿記信息)
"然後放到棧上, 你只能通過api來操作這個值", lua api給c提供了一套完備的操作界面, 這個就相當於約定的通信協議, 如果lua客戶使用這個操作界面, 那麼lua本身不會出現任何"意料之外"的錯誤.
"我只管我的世界"這句話體現了lua和c/c++作為兩個不同系統的分界, c/c++中的值, lua是不知道的, lua只負責它的世界
現在有這樣一個hello.lua 文件:
str = "I am so cool" tbl = {name = "shun", id = 20114442} function add(a,b) return a + b end我們寫一個test.cpp來讀取它:
#include#nclude using namespace std; extern "C" { #include "lua.h" #include "lauxlib.h" #include "lualib.h" } void main() { //1.創建Lua狀態 lua_State *L = luaL_newstate(); if (L == NULL) { return ; } //2.加載Lua文件 int bRet = luaL_loadfile(L,"hello.lua"); if(bRet) { cout<<"load file error"< 知道怎麼讀取後,我們來看下如何修改上面代碼中table的值: // 將需要設置的值設置到棧中 lua_pushstring(L, "我是一個好人~"); // 將這個值設置到table中(此時tbl在棧的位置為2) lua_setfield(L, 2, "name");我們還可以新建一個table:// 創建一個新的table,並壓入棧 lua_newtable(L); // 往table中設置值 lua_pushstring(L, "Give me a girl friend !"); //將值壓入棧 lua_setfield(L, -2, "str"); //將值設置到table中,並將Give me a girl friend 出棧需要注意的是:堆棧操作是基於棧頂的,就是說它只會去操作棧頂的值。
舉個比較簡單的例子,函數調用流程是先將函數入棧,參數入棧,然後用lua_pcall調用函數,此時棧頂為參數,棧底為函數,所以棧過程大致會是:參數出棧->保存參數->參數出棧->保存參數->函數出棧->調用函數->返回結果入棧。
類似的還有lua_setfield,設置一個表的值,肯定要先將值出棧,保存,再去找表的位置。
再不理解可看如下例子:
lua_getglobal(L, "add"); // 獲取函數,壓入棧中 lua_pushnumber(L, 10); // 壓入第一個參數 lua_pushnumber(L, 20); // 壓入第二個參數 int iRet= lua_pcall(L, 2, 1, 0);// 將2個參數出棧,函數出棧,壓入函數返回結果 lua_pushstring(L, "我是一個好人~"); // lua_setfield(L, 2, "name"); // 會將"我是一個大帥鍋~"出棧另外補充一下:
lua_getglobal(L,"var")會執行兩步操作:1.將var放入棧中,2.由Lua去尋找變量var的值,並將變量var的值返回棧頂(替換var)。
lua_getfield(L,-1,"name")的作用等價於 lua_pushstring(L,"name") + lua_gettable(L,-2)
lua value 和 c value的對應關系:
c lua nil 無 {value=0, tt = t_nil} boolean int 非0, 0 {value=非0/0, tt = t_boolean} number int/float等 1.5 {value=1.5, tt = t_number} lightuserdata void*, int*, 各種* point {value=point, tt = t_lightuserdata} string char str[] {value=gco, tt = t_string} gco=TString obj table 無 {value=gco, tt = t_table} gco=Table obj userdata 無 {value=gco, tt = t_udata} gco=Udata obj closure 無 {value=gco, tt = t_function} gco=Closure obj可以看出來, lua中提供的一些類型和c中是對應的, 也提供一些c中沒有的類型. 其中有一些藥特別的說明一下:
nil值, c中沒有對應, 但是可以通過lua_pushnil向lua中壓入一個nil值
注意:lua_push*族函數都有"創建一個類型的值並壓入"的語義, 因為lua中所有的變量都是lua中創建並保存的, 對於那些和c中有對應關系的lua類型, lua會通過api傳來的附加參數, 創建出對應類型的lua變量放在棧頂, 對於c中沒有對應類型的lua類型, lua直接創建出對應變量放在棧頂.
例如: lua_pushstring(L, “string”) lua根據"string"創建一個 TString obj, 綁定到新分配的棧頂元素上
lua_pushcclosure(L,func, 0) lua根據func創建一個 Closure obj, 綁定到新分配的棧頂元素上
lua_pushnumber(L,5) lua直接修改新分配的棧頂元素, 將5賦值到對應的域
lua_createtable(L,0, 0)lua創建一個Tabke obj, 綁定到新分配的棧頂元素上
總之, 這是一個c value –> lua value的流向, 不管是想把一個簡單的5放入lua的世界, 還是創建一個table, 都會導致
1. 棧頂新分配元素 2. 綁定或賦值
還是為了重復一句話, 一個c value入棧就是進入了lua的世界, lua會生成一個對應的結構並管理起來, 從此就不再依賴這個c value
lua value –> c value時, 是通過 lua_to* 族api實現, 很簡單, 取出對應的c中的域的值就行了, 只能轉化那些c中有對應值的lua value, 比如table就不能to c value, 所以api中夜沒有提供 lua_totable這樣的接口.