c++和lua相互調(diào)用

lua語言的功能就不用過多的描述,詳細(xì)的可以去找相關(guān)的lua書籍去看看供屉!這里主要說的是c++和lua相互調(diào)用的情況

1.調(diào)用原理

其實c或c++和lua的交互昧廷,是通過一個虛擬棧進(jìn)行的蝶防。在lua中憔晒,lua堆棧就是一個struct藻肄,堆棧的索引可以是正數(shù),也可以是負(fù)數(shù)拒担,區(qū)別是:正數(shù)索引1永遠(yuǎn)表示棧底嘹屯,負(fù)數(shù)索引-1永遠(yuǎn)表示棧頂。
如圖所示

image

lua的棧類似于以下的定義, 它是在創(chuàng)建lua_State的時候創(chuàng)建的:

TValue stack[max_stack_len] // 欲知內(nèi)情可以查 lstate.cstack_init函數(shù)
存入棧的數(shù)據(jù)類型包括數(shù)值, 字符串, 指針, talbe, 閉包等.

下面是一個棧的例子:

image

執(zhí)行下面的代碼就可以讓你的lua棧上呈現(xiàn)圖中的情況

lua_pushcclosure(L, func, 0) // 創(chuàng)建并壓入一個閉包

lua_createtable(L, 0, 0)// 新建并壓入一個表

lua_pushnumber(L, 123)  // 壓入一個數(shù)字

lua_pushstring(L, “str”)   // 壓入一個字符串

這里要說明的是, 你壓入的類型有數(shù)值, 字符串, 表和閉包[在c中看來是不同類型的值], 但是最后都是統(tǒng)一用TValue這種數(shù)據(jù)結(jié)構(gòu)來保存的:), 下面用圖簡單的說明一下這種數(shù)據(jù)結(jié)構(gòu):

TValue結(jié)構(gòu)對應(yīng)于lua中的所有數(shù)據(jù)類型, 是一個{值, 類型} 結(jié)構(gòu), 這就lua中動態(tài)類型的實現(xiàn), 它把值和類型綁在一起, 用tt記錄value的類型, value是一個聯(lián)合結(jié)構(gòu), 由Value定義, 可以看到這個聯(lián)合有四個域, 先說明簡單下

  • p -- 可以存一個指針, 實際上是lua中的light userdata結(jié)構(gòu)

  • n -- 所有的數(shù)值存在這里, 不過是int , 還是float

  • b -- Boolean值存在這里, 注意, lua_pushinteger不是存在這里, 而是存在n中, b只存布爾

  • gc -- 其他諸如table, thread, closure, string需要內(nèi)存管理垃圾回收的類型都存在這里

gc是一個指針, 它可以指向的類型由聯(lián)合體GCObject定義, 從圖中可以看出, 有string, userdata, closure, table, proto, upvalue, thread

從下面的圖可以的得出如下結(jié)論:

  1. lua中, number, boolean, nil, light userdata四種類型的值是直接存在棧上元素里的, 和垃圾回收無關(guān).

  2. lua中, string, table, closure, userdata, thread存在棧上元素里的只是指針, 他們都會在生命周期結(jié)束后被垃圾回收.

二澎蛛、堆棧的操作

因為Lua與C/C++是通過棧來通信抚垄,Lua提供了C API對棧進(jìn)行操作蜕窿。

我們先來看一個最簡單的例子:

#include <iostream>  
#include <string.h>  
using namespace std;  
   
extern "C"  
{  
  #include "lua.h"  
  #include "lauxlib.h"  
  #include "lualib.h"  
}  
void main()  
{  
  //1.創(chuàng)建一個state  
  lua_State *L = luaL_newstate();  
   
  //2.入棧操作  
  lua_pushstring(L, "I am so cool~");   
  lua_pushnumber(L,20);  
   
  //3.取值操作  
  if( lua_isstring(L,1)){ //判斷是否可以轉(zhuǎn)為string  
  cout<<lua_tostring(L,1)<<endl;  //轉(zhuǎn)為string并返回  
}  
if( lua_isnumber(L,2)){  
  cout<<lua_tonumber(L,2)<<endl;  
}  
   
//4.關(guān)閉state  
lua_close(L);  
return ;  
}

可以簡單理解為luaL_newstate返回一個指向堆棧的指針谋逻,其它看注釋應(yīng)該能懂了吧呆馁。
其他一些棧操作:

int   lua_gettop (lua_State *L);//返回棧頂索引(即棧長度)  
void  lua_settop (lua_State *L, int idx);   //
void  lua_pushvalue (lua_State *L, int idx);//將idx索引上的值的副本壓入棧頂  
void  lua_remove (lua_State *L, int idx);   //移除idx索引上的值  
void  lua_insert (lua_State *L, int idx);   //彈出棧頂元素,并插入索引idx位置  
void  lua_replace (lua_State *L, int idx);  //彈出棧頂元素毁兆,并替換索引idx位置的值

ua_settop將棧頂設(shè)置為一個指定的位置浙滤,即修改棧中元素的數(shù)量。如果值比原棧頂高气堕,則高的部分nil補(bǔ)足纺腊,如果值比原棧低,則原棧高出的部分舍棄茎芭。所以可以用lua_settop(0)來清空棧揖膜。

三、C++調(diào)用Lua

我們經(jīng)趁纷可以使用Lua文件來作配置文件壹粟。類似ini,xml等文件配置信息∷薨伲現(xiàn)在我們來使用C++來讀取Lua文件中的變量趁仙,table,函數(shù)垦页。

lua和c通信時有這樣的約定: 所有的lua中的值由lua來管理, c++中產(chǎn)生的值lua不知道, 類似表達(dá)了這樣一種意思: "如果你(c/c++)想要什么, 你告訴我(lua), 我來產(chǎn)生, 然后放到棧上, 你只能通過api來操作這個值, 我只管我的世界", 這個很重要, 因為:

  • 如果你想要什么, 你告訴我, 我來產(chǎn)生"就可以保證, 凡是lua中的變量, lua要負(fù)責(zé)這些變量的生命周期和垃圾回收, 所以, 必須由lua來創(chuàng)建這些值(在創(chuàng)建時就加入了生命周期管理要用到的簿記信息)

  • 然后放到棧上, 你只能通過api來操作這個值", lua api給c提供了一套完備的操作界面, 這個就相當(dāng)于約定的通信協(xié)議, 如果lua客戶使用這個操作界面, 那么lua本身不會出現(xiàn)任何"意料之外"的錯誤.

  • 我只管我的世界"這句話體現(xiàn)了lua和c/c++作為兩個不同系統(tǒng)的分界, c/c++中的值, lua是不知道的, lua只負(fù)責(zé)它的世界

現(xiàn)在有這樣一個hello.lua 文件:

str = "I am so cool"  
tbl = {name = "shun", id = 20114442}  
function add(a,b)  
return a + b  
end

我們寫一個test.cpp來讀取它:

#include <iostream>  
#include <string.h>  
using namespace std;  
   
extern "C"  
{  
  #include "lua.h"  
  #include "lauxlib.h"  
  #include "lualib.h"  
}  
void main()  
{  
    //1.創(chuàng)建Lua狀態(tài)  
    lua_State *L = luaL_newstate();  
    if (L == NULL)  
    {  
    return ;  
    }  
   
    //2.加載Lua文件  
    int bRet = luaL_loadfile(L,"hello.lua");  
    if(bRet)  
    {  
        cout<<"load file error"<<endl;  
        return ;  
    }  
   
    //3.運(yùn)行Lua文件  
    bRet = lua_pcall(L,0,0,0);  
    if(bRet)  
    {  
        cout<<"pcall error"<<endl;  
        return ;  
    }  
   
    //4.讀取變量  
    lua_getglobal(L,"str");  
    string str = lua_tostring(L,-1);  
    cout<<"str = "<<str.c_str()<<endl;//str = I am so cool~  
   
    //5.讀取table  
    lua_getglobal(L,"tbl");   
    lua_getfield(L,-1,"name");  
    str = lua_tostring(L,-1);  
    cout<<"tbl:name = "<<str.c_str()<<endl; //tbl:name = shun  
   
    //6.讀取函數(shù)  
    lua_getglobal(L, "add");// 獲取函數(shù)雀费,壓入棧中  
    lua_pushnumber(L, 10);  // 壓入第一個參數(shù)  
    lua_pushnumber(L, 20);  // 壓入第二個參數(shù)  
    int iRet= lua_pcall(L, 2, 1, 0);// 調(diào)用函數(shù),調(diào)用完成以后痊焊,會將返回值壓入棧中盏袄,2表示參數(shù)個數(shù),1表示返回結(jié)果個數(shù)薄啥。  
    if (iRet)   // 調(diào)用出錯  
    {  
        const char *pErrorMsg = lua_tostring(L, -1);  
        cout << pErrorMsg << endl;  
        lua_close(L);  
        return ;  
    }  
    if (lua_isnumber(L, -1))//取值輸出  
    {  
        double fValue = lua_tonumber(L, -1);  
        cout << "Result is " << fValue << endl;  
    }  
   
    //至此貌矿,棧中的情況是:  
    //=================== 棧頂 ===================   
    //  索引  類型  值  
    //   4   int:  30   
    //   3   string:   shun   
    //   2   table: tbl  
    //   1   string:I am so cool~  
    //=================== 棧底 ===================   
   
    //7.關(guān)閉state  
    lua_close(L);  
    return ;  
}

知道怎么讀取后,我們來看下如何修改上面代碼中table的值:

// 將需要設(shè)置的值設(shè)置到棧中  
lua_pushstring(L, "我是一個大帥鍋~");
// 將這個值設(shè)置到table中(此時tbl在棧的位置為2)  
lua_setfield(L, 2, "name");

我們還可以新建一個table:

// 創(chuàng)建一個新的table罪佳,并壓入棧  
lua_newtable(L); 
// 往table中設(shè)置值  
lua_pushstring(L, "Give me a girl friend !"); //將值壓入棧  
lua_setfield(L, -2, "str"); //將值設(shè)置到table中逛漫,并將Give me a girl friend 出棧

需要注意的是:堆棧操作是基于棧頂?shù)模褪钦f它只會去操作棧頂?shù)闹怠?/p>

舉個比較簡單的例子赘艳,函數(shù)調(diào)用流程是先將函數(shù)入棧酌毡,參數(shù)入棧,然后用lua_pcall調(diào)用函數(shù)蕾管,此時棧頂為參數(shù)枷踏,棧底為函數(shù),所以棧過程大致會是:參數(shù)出棧->保存參數(shù)->參數(shù)出棧->保存參數(shù)->函數(shù)出棧->調(diào)用函數(shù)->返回結(jié)果入棧掰曾。

類似的還有l(wèi)ua_setfield旭蠕,設(shè)置一個表的值,肯定要先將值出棧,保存掏熬,再去找表的位置佑稠。

再不理解可看如下例子:

lua_getglobal(L, "add");// 獲取函數(shù),壓入棧中  
lua_pushnumber(L, 10);  // 壓入第一個參數(shù)  
lua_pushnumber(L, 20);  // 壓入第二個參數(shù)  
int iRet= lua_pcall(L, 2, 1, 0);// 將2個參數(shù)出棧旗芬,函數(shù)出棧舌胶,壓入函數(shù)返回結(jié)果  
lua_pushstring(L, "我是一個大帥鍋~");  //   
lua_setfield(L, 2, "name"); // 會將"我是一個大帥鍋~"出棧

另外補(bǔ)充一下:

lua_getglobal(L,"var")會執(zhí)行兩步操作:1.將var放入棧中,2.由Lua去尋找變量var的值疮丛,并將變量var的值返回棧頂(替換var)幔嫂。

lua_getfield(L,-1,"name")的作用等價于 lua_pushstring(L,"name") + lua_gettable(L,-2)

lua value 和 c value的對應(yīng)關(guān)系:

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中是對應(yīng)的, 也提供一些c中沒有的類型. 其中有一些要特別的說明一下:

nil值, c中沒有對應(yīng), 但是可以通過lua_pushnil向lua中壓入一個nil值

注意: lua_push*族函數(shù)都有"創(chuàng)建一個類型的值并壓入"的語義, 因為lua中所有的變量都是lua中創(chuàng)建并保存的, 對于那些和c中有對應(yīng)關(guān)系的lua類型, lua會通過api傳來的附加參數(shù), 創(chuàng)建出對應(yīng)類型的lua變量放在棧頂, 對于c中沒有對應(yīng)類型的lua類型, lua直接創(chuàng)建出對應(yīng)變量放在棧頂.

例如:

  • lua_pushstring(L, “string”) lua根據(jù)"string"創(chuàng)建一個 TString obj, 綁定到新分配的棧頂元素上
  • lua_pushcclosure(L,func, 0) lua根據(jù)func創(chuàng)建一個 Closure obj, 綁定到新分配的棧頂元素上
  • lua_pushnumber(L,5) lua直接修改新分配的棧頂元素, 將5賦值到對應(yīng)的域
  • lua_createtable(L,0, 0)lua創(chuàng)建一個Tabke obj, 綁定到新分配的棧頂元素上

總之, 這是一個 c value –> lua value的流向, 不管是想把一個簡單的5放入lua的世界, 還是創(chuàng)建一個table, 都會導(dǎo)致

  1. 棧頂新分配元素
  2. 綁定或賦值

還是為了重復(fù)一句話, 一個c value入棧就是進(jìn)入了lua的世界, lua會生成一個對應(yīng)的結(jié)構(gòu)并管理起來, 從此就不再依賴這個c value

lua value –> c value時, 是通過 lua_to* 族api實現(xiàn), 很簡單, 取出對應(yīng)的c中的域的值就行了, 只能轉(zhuǎn)化那些c中有對應(yīng)值的lua value, 比如table就不能to c value, 所以api中夜沒有提供 lua_totable這樣的接口.

四誊薄、Lua調(diào)用C++

lua調(diào)用c或c++在項目中很少用得著履恩!所以這里就不用再講了!

總結(jié)

c或c++調(diào)用的lua是項目中經(jīng)常用到的呢蔫,特別是在游戲方面似袁,做熱更新。其他項目中有一大部分是用再讀取配置配置文件咐刨,替代常用的配置文件類型(ini昙衅,conf,xml等)定鸟,用lua做配置的優(yōu)點(diǎn)有以下幾個:

  • lua做配置文件必須遵循lua語法
  • 調(diào)用lua的API能處理lua中的錯誤語法
  • 調(diào)用lua的API能跳過lua中的注釋
  • 調(diào)用API相對簡單
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末而涉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子联予,更是在濱河造成了極大的恐慌啼县,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沸久,死亡現(xiàn)場離奇詭異季眷,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)卷胯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門子刮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人窑睁,你說我怎么就攤上這事挺峡。” “怎么了担钮?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵橱赠,是天一觀的道長。 經(jīng)常有香客問我箫津,道長狭姨,這世上最難降的妖魔是什么宰啦? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮饼拍,結(jié)果婚禮上赡模,老公的妹妹穿的比我還像新娘。我一直安慰自己惕耕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布诫肠。 她就那樣靜靜地躺著司澎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪栋豫。 梳的紋絲不亂的頭發(fā)上挤安,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天,我揣著相機(jī)與錄音丧鸯,去河邊找鬼蛤铜。 笑死,一個胖子當(dāng)著我的面吹牛丛肢,可吹牛的內(nèi)容都是我干的围肥。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蜂怎,長吁一口氣:“原來是場噩夢啊……” “哼穆刻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起杠步,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤氢伟,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后幽歼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體朵锣,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年甸私,在試婚紗的時候發(fā)現(xiàn)自己被綠了诚些。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡皇型,死狀恐怖泣刹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情犀被,我是刑警寧澤椅您,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站寡键,受9級特大地震影響掀泳,放射性物質(zhì)發(fā)生泄漏雪隧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一员舵、第九天 我趴在偏房一處隱蔽的房頂上張望脑沿。 院中可真熱鬧,春花似錦马僻、人聲如沸庄拇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽措近。三九已至,卻和暖如春女淑,著一層夾襖步出監(jiān)牢的瞬間瞭郑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工鸭你, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留屈张,地道東北人。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓袱巨,卻偏偏與公主長得像阁谆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子愉老,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 13,798評論 0 38
  • 名稱 說明 docLua 相關(guān)的文檔笛厦,包括了編譯文檔、接口文檔等 Makefile 編譯Lua使用俺夕,在...
    一川煙草i蓑衣閱讀 1,446評論 0 1
  • 1. 寫在前面 很多時候我們都需要借助一些腳本語言來為我們實現(xiàn)一些動態(tài)的配置裳凸,那么就會涉及到如何讓腳本語言跟原生語...
    杰嗒嗒的阿杰閱讀 3,432評論 9 31
  • 模擬器截屏統(tǒng)一在command + 1的情況下(實際屏幕大小)劝贸,否則提示圖片錯誤 模擬器截屏快捷鍵command ...
    GreenB閱讀 1,276評論 0 0
  • 人生就像分析化學(xué)中的滴定姨谷,每個人在滴定終點(diǎn)到來之前都在不停地旋轉(zhuǎn)。 大學(xué)分析化學(xué)課的時候映九,我們每周都有滴定實驗梦湘,當(dāng)...
    玥玥080166閱讀 618評論 0 0