日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[转载]Lua脚本与C++交互

發(fā)布時間:2025/3/15 编程问答 17 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [转载]Lua脚本与C++交互 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Lua腳本在C++下的舞步(入門指引)(轉)

Lua腳本在C++下的舞步(一)(入門指引)

轉帖來自:http://www.acejoy.com/bbs/viewthread.php?tid=1931&extra=page%3D1

現(xiàn)在,越來越多的C++服務器和客戶端融入了腳本的支持,尤其在網(wǎng)游領域,腳本語言已經(jīng)滲透到了方方面面,比如你可以在你的客戶端增加一個腳本,這個腳本將會幫你在界面上顯示新的數(shù)據(jù),亦或幫你完成某些任務,亦或幫你查看別的玩家或者NPC的狀態(tài)。。。如此等等。

但是我覺得,其實腳本語言與C++的結合,遠遠比你在游戲中看到的特效要來的迅猛。它可以運用到方方面面的領域,比如你最常見的應用領域。比如,你 可以用文本編輯器,寫一個腳本語言,然后用你的程序加載一下,就會產生出很絢麗的界面。亦或一兩句文本語言,就會讓你的程序發(fā)送數(shù)據(jù)給服務器,是不是很酷 呢?
本來我想,寫一篇關于主流腳本語言Lua和Python的文章,但是感覺這樣過于乏味,于是分開來一一介紹,相信對C++了解的你,看過我 的文章后會對腳本語言這種東西產生濃厚的興趣,我想起以前聽的一個故事,當年Java的創(chuàng)造者講課的時候,一開始先拿一個簡單的不能簡單的小例子,不斷的 擴展,最后成為一個復雜而完美的程序。今天我也就這樣實驗一下吧,呵呵。

當然,我本人不敢說對腳本語言了如指掌,只能說略微掌握一些,用過幾年,偏頗之處請大家指正。
下面,開始吧,先說LUA!(本文面向初學者)

Lua語言(http://www.lua.org/),想必不少程序員都 聽過,據(jù)我所知,由于《魔獸世界》里面對它的加載,它一下子變成了很多游戲開發(fā)者競相研究的對象,至于這個巴西創(chuàng)造者么,我不過多介紹,大家有興趣可以谷 歌一下。其實網(wǎng)上有很多關于lua的教材和例子,說真的,對于當年的我而言,幾乎看不懂,當時很郁悶,感覺Lua復雜的要命,有些懼怕,后來沉下心來一點 點研究,覺得其實還是蠻簡潔的。只是網(wǎng)上的資料或許偏向于某些功能,導致了邏輯和代碼的復雜。后來總結,其實學習一種腳本語言,完全可以抱著放松的心態(tài)一 點點的研究,反而效果會更好。

在講代碼之前,我要說Lua的一些特點,這些特點有利于你在復雜的代碼調用中,清晰的掌握中間的來龍去脈。實際上,你能常常用到的lua的API, 不過超過10個,再復雜的邏輯。基本上也是這么多API組成的。至于它們是什么,下面的文章會介紹。另外一個重要之重要的概念,就是棧。Lua與別的語言 交互以及交換數(shù)據(jù),是通過棧完成的。其實簡單的解釋一下,你可以把棧想象成一個箱子,你要給他數(shù)據(jù),就要按順序一個個的把數(shù)據(jù)放進去,當然,Lua執(zhí)行完 畢,可能會有結果返回給你,那么Lua還會利用你的箱子,一個個的繼續(xù)放下去。而你取出返回數(shù)據(jù)呢,要從箱子頂上取出,如果你想要獲得你的輸入?yún)?shù)呢?那 也很簡單,按照頂上返回數(shù)據(jù)的個數(shù),再按順序一個個的取出,就行了。不過這里提醒大家,關于棧的位置,永遠是相對的,比如-1代表的是當前棧頂,-2代表 的是當前棧頂下一個數(shù)據(jù)的位置。棧是數(shù)據(jù)交換的地方,一定要有一些棧的概念。

好了,基礎的lua語法不在這里講,百度一下有很多。
先去http://www.lua.org/ 去下載一個最新的Lua代碼(現(xiàn)在穩(wěn)定版是lua-5.1.4)。它的代碼是用C寫的,所以很容易兼容很多平臺。
在linux下,目錄src下就有專門的Makefile。很簡單,啥都不用做,指定一下位置編譯即可。
在 windows下,以VS2005為例,建立一個空的靜態(tài)庫工程(最好不使用預編譯頭,把預編譯頭的選項勾去掉),然后把src下的所有文件(除了 Makefile)一股腦拷到工程中去。然后將這些文件添加到你的工程中,編譯,會生成一個*.llib(*是你起的lua庫名),行了,建立一個目錄 lib,把它拷過去,然后再建立一個include的文件夾,把你工程目錄下的lua.h,lualib.h,lauxlib.h,拷貝過去。行了,拿著 這兩個文件夾,你就可以在你的工程里使用lua了。
行了,材料齊了,我們來看看怎么寫一個簡單的lua程序吧。

建立一個文件,起名Sample.lua
里面添加這樣的代碼。
function func_Add(x, y)
?? return x+y;
end

這是一個標準的lua語法,一個函數(shù),實現(xiàn)簡單的a+b操作,并返回操作結果。
保存退出。
多一句嘴,在Lua里面,是可以支持多數(shù)據(jù)返回的。
比如你這么寫:
function func_Add(x, y)
?? return x+y, x-y;
end

意思是返回第一個參數(shù)是相加的結果,第二個是相減的結果,也是可以的。在lua里面沒有類型的概念。當然,在C++接受這樣的返回值的時候,也很簡單,請往下看。
好了,材料齊備了,咱們來看看C++程序怎么調用它。
首先,建立一個類,負責加載這個lua文件,并執(zhí)行函數(shù)操作,我們姑且叫做CLuaFn
要加載這個lua文件,按照正常的思路,我們應該先加載,然后再調用不同的函數(shù)。恩,對了,咱們就這么做。

extern “C”
{
??????? #include “l(fā)ua.h”
??????? #include “l(fā)ualib.h”
??????? #include “l(fā)auxlib.h”
};

class CLuaFn
{
public:
??????? CLuaFn(void);
??????? ~CLuaFn(void);

??????? void Init();??????????? //初始化Lua對象指針參數(shù)
??????? void Close();???????? //關閉Lua對象指針

??????? bool LoadLuaFile(const char* pFileName);????????????????????????????? //加載指定的Lua文件
??????? bool CallFileFn(const char* pFunctionName, int nParam1, int nParam2);??????? //執(zhí)行指定Lua文件中的函數(shù)

private:
??????? lua_State* m_pState;?? //這個是Lua的State對象指針,你可以一個lua文件對應一個。
};

恩,頭文件就這么多,看看,一點也不復雜吧,看了cpp我想你會更高興,因為代碼一樣很少。我一個個函數(shù)給你們介紹。

void CLuaFn::Init()
{
??????? if(NULL == m_pState)
??????? {
??????????????? m_pState = lua_open();
??????????????? luaL_openlibs(m_pState);
??????? }
}

初始化函數(shù),標準代碼,沒啥好說的,lua_open()是返回給你一個lua對象指針,luaL_openlibs()是一個好東西,在 lua4,初始化要做一大堆的代碼,比如加載lua的string庫,io庫,math庫等等等等,代碼洋洋灑灑一大堆,其實都是不必要的,因為這些庫你 基本都需要用到,除了練習你的打字能力別的意義不大,因為代碼寫法都是固定的。于是在5以后,Lua的創(chuàng)造者修改了很多,這就是其一,一句話幫你加載了所 有你可能用到的Lua基本庫。

void CLuaFn::Close()
{
??????? if(NULL != m_pState)
??????? {
??????????????? lua_close(m_pState);
??????????????? m_pState = NULL;
??????? }
}

顧名思義,我用完了,關閉我的Lua對象并釋放資源。呵呵,標準寫法,沒啥好說的。

bool CLuaFn:: LoadLuaFile(const char* pFileName)
{
??????? int nRet = 0;
??????? if(NULL == m_pState)
??????? {
??????????????? printf(“[CLuaFn:: LoadLuaFile]m_pState is NULL./n”);
??????????????? return false;
??????? }

??????? nRet = luaL_dofile(m_pState, pFileName);
??????? if (nRet != 0)
??????? {
??????????????? printf(“[CLuaFn:: LoadLuaFile]luaL_loadfile(%s) is file(%d)(%s)./n”, pFileName, nRet, lua_tostring(m_pState, -1));
??????????????? return false;
??????? }

??????? return true;
}

呵呵,這個有點意思,加載一個Lua文件。
這里我要詳細的說一下,因為Lua是腳本語言,加載lua文件本身的時候才會編譯。
所 以,推薦大家在加載文件的時候盡量放在程序的初始化中,因為當你執(zhí)行l(wèi)uaL_dofile()函數(shù)的時候,Lua會啟用語法分析器,去分析你的腳本語法 是否符合Lua規(guī)則,如果你胡亂的傳一個文件過去,Lua就會告訴你文件語法錯誤,無法加載。如果你的Lua腳本很大,函數(shù)很多,語法分析器會比較耗時, 所以,加載的時候,盡量放在合適的地方,而且,對于一個Lua文件而言,反復加載luaL_dofile()除了會使你的CPU變熱沒有任何意義。

或許你對printf(“[CLuaFn:: LoadLuaFile]luaL_loadfile(%s) is file(%d)(%s)./n”, pFileName, nRet, lua_tostring(m_pState, -1));這句話很感興趣,這個在干什么?這里我先說lua_tostring(m_pState, -1)這是在干什么,還記得我說的Lua是基于棧傳輸數(shù)據(jù)的么?那么,如果報錯,我怎么知道錯誤是什么?luaL_dofile標準返回一個int,我總 不能到lua.h里面遍歷這個nRet 是啥意思吧,恩,Lua創(chuàng)造者早就為你想好了,只不過你需要稍微動一下你的腦筋。Lua的創(chuàng)造者在語法分析器分析你的語法的時候,發(fā)現(xiàn)錯誤,會有一段文字 告訴你是什么錯誤,它會把這個字符串放在棧頂。那么,怎么取得棧頂?shù)淖址?#xff1f;lua_tostring(m_pState, -1)就可以,-1代表的是當前棧的位置是相對棧頂。當然,你也可以看看棧里面還有一些什么其他古怪的數(shù)據(jù),你可以用1,2,3(這些是絕對位置,而-1 是相對位置)去嘗試,呵呵。不過,相信你得到的也很難看懂,因為一個Lua對象執(zhí)行的時候,會用很多次棧進行數(shù)據(jù)交換,而你看到的,有可能是交換中的數(shù) 據(jù)。那么,話說回來,這句話的意思就是”[CLuaFn:: LoadLuaFile]luaL_loadfile(文件名) is file(錯誤編號)(錯誤具體描述文字)./n”

bool CLuaFn::CallFileFn(const char* pFunctionName, int nParam1, int nParam2)
{
??????? int nRet = 0;
??????? if(NULL == m_pState)
??????? {
??????????????? printf(“[CLuaFn::CallFileFn]m_pState is NULL./n”);
??????????????? return false;
??????? }

??????? lua_getglobal(m_pState, pFunctionName);

??????? lua_pushnumber(m_pState, nParam1);
??????? lua_pushnumber(m_pState, nParam2);

??????? nRet = lua_pcall(m_pState, 2, 1, 0);
??????? if (nRet != 0)
??????? {
??????????????? printf(“[CLuaFn::CallFileFn]call function(%s) error(%d)./n”, pFunctionName, nRet);
??????????????? return false;
??????? }

??????? if (lua_isnumber(m_pState, -1) == 1)
??????? {
??????????????? int nSum = lua_tonumber(m_pState, -1);
??????????????? printf(“[CLuaFn::CallFileFn]Sum = %d./n”, nSum);
??????? }

??????? return true;
}

這個函數(shù)是,傳入函數(shù)名稱和參數(shù),去你的Lua文件中去執(zhí)行。
lua_getglobal(m_pState, pFunctionName);
這個函數(shù)是驗證你的Lua函數(shù)是否在你當前加載的Lua文件中,并把指針指向這個函數(shù)位置。

lua_pushnumber(m_pState, nParam1);?? <—對應你的x參數(shù)
lua_pushnumber(m_pState, nParam2);?? <—對應你的y參數(shù)

這就是著名的壓棧操作了,把你的參數(shù)壓入Lua的數(shù)據(jù)棧。供Lua語法器去獲得你的數(shù)據(jù)。
lua_pushnumber()是一個壓入數(shù)字,lua_pushstring()是壓入一個字符串。。。

那么你會問,如果我有一個自己的類型,一個類指針或者別的什么,我怎么壓入?別著急,方法當然是有的,呵呵,不過你先看看如果簡單的如何做,在下幾講中,我會告訴你更強大的Lua壓棧藝術。
這里需要注意的是,壓棧的順序,對,簡單說,就是從左到右的參數(shù),左邊的先進棧,右邊的最后進棧。

nRet = lua_pcall(m_pState, 2, 1, 0);
這句話的意思是,執(zhí)行這個函數(shù),2是輸入?yún)?shù)的個數(shù),1是輸出參數(shù)的個數(shù)。當然,如果你把Lua函數(shù)改成
return x+y, x-y;
代碼需要改成nRet = lua_pcall(m_pState, 2, 2, 0);
明白了吧,呵呵,很簡單吧。
當然,如果函數(shù)執(zhí)行失敗,會觸發(fā)nRet,我這里偷了個懶,如果你想得到為什么錯了?可以用lua_tostring(m_pState, -1)去棧頂找,明白?是不是有點感覺了?

lua_isnumber(m_pState, -1)
這句話是判定棧頂?shù)脑厥遣皇菙?shù)字。因為如果執(zhí)行成功,棧頂就應該是你的數(shù)據(jù)返回值。
int nSum = lua_tonumber(m_pState, -1);
printf(“[CLuaFn::CallFileFn]Sum = %d./n”, nSum);
這個nSum就是返回的結果。
當然,你會問,如果 return x+y, x-y;我該怎么辦?
int nSum = lua_tonumber(m_pState, -1);
int nSub = lua_tonumber(m_pState, -2);
搞定,看見沒。按照壓棧順序。呵呵,是不是又有感覺了,對,棧就是數(shù)據(jù)交互的核心。對Lua的理解程度和運用技巧,其實就是對棧的靈活運用和操作。
好了。你的第一個Lua程序大功告成!竟然不是Hello world,呵呵。
好了,我們看看Main函數(shù)怎么寫吧,相信大家都會寫。

#include “LuaFn.h”

int _tmain(int argc, _TCHAR* argv[])
{
??????? CLuaFn LuaFn;

??????? //LuaFn.InitClass();

??????? LuaFn.LoadLuaFile(“Sample.lua”);
??????? LuaFn.CallFileFn(“func_Add”, 11, 12);
??????? getchar();

??????? return 0;
}

行了,Build一下,看看,是不是你要的結果?如果是,賀喜你,你已經(jīng)邁出了Lua的第一步。
洋洋灑灑寫了一個小時,喝口水吧,呵呵, 下一講,我將強化這個LuaFn類,讓它給我做更多的事情。呵呵,最后,我會讓你打到,用Lua文件直接畫出一個Windows窗體來。并在上面畫出各種 按鈕,列表,以及復選框。是不是感覺很酷?用文本去創(chuàng)造一個程序?很激動吧,恩,確實,Lua能給你做到。只要你有耐心看下去。。。

Lua腳本在C++下的舞步(二)

上一節(jié)講了一些基本的Lua應用,或許你會說,還是很簡單么。呵呵,恩,是的,本來Lua就是為了讓大家使用的方便快捷而設計的。如果設計的過為復雜,就不會有人使用了。
下面,我要強調一下,Lua的棧的一些概念,因為這個確實很重要,你會經(jīng)常用到。熟練使用Lua,最重要的就是要時刻知道什么時候棧里面的數(shù)據(jù)是什么順序,都是什么。如果你能熟練知道這些,實際你已經(jīng)是Lua運用的高手了。
說 真的,第一次我接觸棧的時候,沒有把它想的很復雜,倒是看了網(wǎng)上很多的關于Lua的文章讓我對棧的理解云里霧里,什么元表,什么User,什么局部變量, 什么全局變量位移。說的那叫一個暈。本人腦子笨,理解不了這么多,也不知道為什么很多人喜歡把Lua棧弄的七上八下,代碼晦澀難懂。后來實在受不了了,去 Lua網(wǎng)站下載了Lua的文檔,寫的很清晰。Lua的棧實際上幾句話足以。
當你初始化一個棧的時候,它的棧底是1,而棧頂相對位置是-1,說形 象一些,你可以把棧想象成一個環(huán),有一個指針標記當前位置,如果-1,就是當前棧頂,如果是-2就是當前棧頂前面一個參數(shù)的位置。以此類推。當然,你也可 以正序去取,這里要注意,對于Lua的很多API,下標是從1開始的。這個和C++有些不同。而且,在棧的下標中,正數(shù)表示絕對棧底的下標,負數(shù)表示相對 棧頂?shù)南鄬Φ刂?#xff0c;這個一定要有清晰的概念,否則很容易看暈了。
讓我們看一些例子,加深理解。

lua_pushnumber(m_pState, 11);
lua_pushnumber(m_pState, 12);

int nIn = lua_gettop(m_pState);? <–這里加了一行, lua_gettop()這個API是告訴你目前棧里元素的個數(shù)。
如果僅僅是Push兩個參數(shù),那么nIn的數(shù)值是2,對。沒錯。那么咱們看看棧里面是怎么放的。我再加兩行代碼。

lua_pushnumber(m_pState, 11);
lua_pushnumber(m_pState, 12);

int nIn = lua_gettop(m_pState)

int nData1 = lua_tonumber(m_pState, 1);???? <–讀取棧底第一個絕對坐標中的元素
int nData2 = lua_tonumber(m_pState, 2);???? <–讀取棧底第二個絕對坐標中的元素
printf(“[Test]nData1? = %d, nData2? = %d./n”);
如果是你,憑直覺,告訴我答案是什么?
現(xiàn)在公布答案,看看是不是和你想的一樣。
[Test]nData1? = 11, nData2? = 12
呵呵,那么,如果我把代碼換成

lua_pushnumber(m_pState, 11);
lua_pushnumber(m_pState, 12);

int nIn = lua_gettop(m_pState)

int nData1 = lua_tonumber(m_pState, -1);???? <–讀取棧頂?shù)谝粋€相對坐標中的元素
int nData2 = lua_tonumber(m_pState, -2);???? <–讀取棧頂?shù)诙€相對坐標中的元素
printf(“[Test]nData1? = %d, nData2? = %d./n”);

請你告訴我輸出是什么?
答案是
[Test]nData1? = 12, nData2? = 11
呵呵,挺簡單的吧,對了,其實就這么簡單。網(wǎng)上其它的高階運用,其實大部分都是對棧的位置進行調整。只要你抓住主要概念,看懂還是不難的。什么元表,什么變量,其實都一樣,抓住核心,時刻知道棧里面的樣子,就沒有問題。
好了,回到我上一節(jié)的那個代碼。

bool CLuaFn::CallFileFn(const char* pFunctionName, int nParam1, int nParam2)
{
??????? int nRet = 0;
??????? if(NULL == m_pState)
??????? {
??????????????? printf(“[CLuaFn::CallFileFn]m_pState is NULL./n”);
??????????????? return false;
??????? }

??????? lua_getglobal(m_pState, pFunctionName);

??????? lua_pushnumber(m_pState, nParam1);
??????? lua_pushnumber(m_pState, nParam2);

??????? int nIn = lua_gettop(m_pState); <–在這里加一行。

??????? nRet = lua_pcall(m_pState, 2, 1, 0);
??????? if (nRet != 0)
??????? {
??????????????? printf(“[CLuaFn::CallFileFn]call function(%s) error(%d)./n”, pFunctionName, nRet);
??????????????? return false;
??????? }

??????? if (lua_isnumber(m_pState, -1) == 1)
??????? {
??????????????? int nSum = lua_tonumber(m_pState, -1);
??????????????? printf(“[CLuaFn::CallFileFn]Sum = %d./n”, nSum);
??????? }

??????? int nOut = lua_gettop(m_pState); <–在這里加一行。

??????? return true;
}

nIn的答案是多少?或許你會說是2吧,呵呵,實際是3。或許你會問,為什么會多一個?其實我第一次看到這個數(shù)字,也很詫異。但是確實是3。因為你 調用的函數(shù)名稱占據(jù)了一個堆棧的位置。其實,在獲取nIn那一刻,堆棧的樣子是這樣的(函數(shù)接口地址,參數(shù)1,參數(shù)2),函數(shù)名稱也是一個變量入棧的。而 nOut輸出是1,lua_pcall()函數(shù)在調用成功之后,會自動的清空棧,然后把結果放入棧中。在獲取nOut的一刻,棧內是這幅摸樣(輸出參數(shù) 1)。
這里就要再遷出一個更重要的概念了,Lua不是C++,對于C++程序員而言,一個函數(shù)會自動創(chuàng)建棧,當函數(shù)執(zhí)行完畢后會自動清理 棧,Lua可不會給你這么做,對于Lua而言,它沒有函數(shù)這個概念,一個棧對應一個lua_State指針,也就是說,你必須手動去清理你不用的棧,否則 會造成垃圾數(shù)據(jù)占據(jù)你的內存。
不信?那么咱們來驗證一下,就拿昨天的代碼吧,你用for循環(huán)調用100萬次。看看nOut的輸出結果。。我相信,程序執(zhí)行不到100萬次就會崩潰,而你的內存也會變的碩大無比。而nOut的輸出也會是這樣的 1,2,3,4,5,6。。。。。
原因就是,Lua不會清除你以前棧內的數(shù)據(jù),每調用一次都會給你生成一個新的棧元素插入其中。
那么怎么解決呢?呵呵,其實,如果不考慮多線程的話,在你的函數(shù)最后退出前加一句話,就可以輕松解決這個問題。(Lua棧操作是非線程安全的!)

lua_settop(m_pState, -2);
這句話的意思是什么?lua_settop()是設置棧頂?shù)奈恢?#xff0c;我這么寫,意思就是,棧頂指針目前在當前位置的-2的元素上。這樣,我就實現(xiàn)了對棧的清除。仔細想一下,是不是這個道理呢?

bool CLuaFn::CallFileFn(const char* pFunctionName, int nParam1, int nParam2)
{
??????? int nRet = 0;
??????? if(NULL == m_pState)
??????? {
??????????????? printf(“[CLuaFn::CallFileFn]m_pState is NULL./n”);
??????????????? return false;
??????? }

??????? lua_getglobal(m_pState, pFunctionName);

??????? lua_pushnumber(m_pState, nParam1);
??????? lua_pushnumber(m_pState, nParam2);

??????? int nIn = lua_gettop(m_pState); <–在這里加一行。

??????? nRet = lua_pcall(m_pState, 2, 1, 0);
??????? if (nRet != 0)
??????? {
??????????????? printf(“[CLuaFn::CallFileFn]call function(%s) error(%d)./n”, pFunctionName, nRet);
??????????????? return false;
??????? }

??????? if (lua_isnumber(m_pState, -1) == 1)
??????? {
??????????????? int nSum = lua_tonumber(m_pState, -1);
??????????????? printf(“[CLuaFn::CallFileFn]Sum = %d./n”, nSum);
??????? }

??????? int nOut = lua_gettop(m_pState); <–在這里加一行。
??????? lua_settop(m_pState, -2);???????????? <–清除不用的棧。

??????? return true;
}

好了,再讓我們運行100萬次,看看你的程序內存,看看你的程序還崩潰不?
如果你想打印 nOut的話,輸出會變成1,1,1,1,1。。。。
最后說一句,lua_tonumber()或lua_tostring()還有以后我們要用到的lua_touserdata()一定要將數(shù)據(jù)完全取出后保存到你的別的變量中去,否則會因為清棧操作,導致你的程序異常,切記!

呵呵,說了這么多,主要是讓大家如何寫一個嚴謹?shù)腖ua程序,不要運行沒兩下就崩潰了。好了,基礎棧的知識先說到這里,以后還有一些技巧的運用,到時候會給大家展示。
下面說一下,Lua的工具。(為什么要說這個呢?呵呵,因為我們下一步要用到其中的一個幫助我們的開發(fā)。)
呵呵,其實,Lua里面有很多簡化開發(fā)的工具,你可以去http://www.sourceforge.net/去找一下。它們能夠幫助你簡化C++對象與Lua對象互轉之間的代碼。
這里說幾個有名的,當然可能不全。

(lua tinker)如果你的系統(tǒng)在windows下,而且不考慮移植,那么我強烈推薦你去下載一個叫做lua tinker的小工具,整個工具非常簡單,一個.h和一個.cpp。直接就可以引用到你的工程中,連獨立編譯都不用,這是一個韓國人寫的Lua與 C++接口轉換的類,十分方便,代碼簡潔(居家旅行,必備良藥)。它是基于模板的,所以你可以很輕松的把你的C++對象綁定到Lua中。代碼較長,呵呵, 有興趣的朋友可以給我留言索要lua tinker的例子。就不貼在這里了。不過我個人不推薦這個東西,因為它在Linux下是編譯不過去的。它使用了一種g++不支持的模板寫法,雖然有人在 嘗試把它修改到Linux下編譯,但據(jù)我所知,修改后效果較好的似乎還沒有。不過如果你只是在? windows下,那就沒什么可猶豫的,強烈推薦,你會喜歡它的。

(Luabinder)相信用過Boost庫的朋友,或許對這個家伙很熟悉。它是一個很強大的Linux下Lua擴展包,幫你封裝了很多Lua的復 雜操作,主要解決了綁定C++對象和Lua對象互動的關系,非常強大,不過嘛,對于freeeyes而言,還是不推薦,因為freeeyes很懶,不想為 了一個Lua還要去編譯一個龐大的boost庫,當然,見仁見智,如果你的程序本身就已經(jīng)加載了boost,那么就應該毫不猶豫的選擇它。

(lua++)呵呵,這是我最喜歡,也是我一直用到現(xiàn)在的庫,比較前兩個而言,lua++的封裝性沒有那么好,很多東西還是需要一點代碼的,不過之 所以我喜歡,是因為它是用C寫的,可以在windows下和linux下輕松轉換。如果魚與熊掌不能兼得,那么我寧愿選擇一個兼顧兩者的東西,如果有的 話,呵呵。當然,lua++就是這么一個東西,如果你繼續(xù)看我的文章,或許你也會喜歡它的。

好了,廢話少說,就讓我選擇lua++作為我們繼續(xù)進行下去的墊腳石吧。
說到Lua++(http://www.codenix.com/~tolua/),這個東西還是挺有淵源的,請你先下載一個。我教你怎么編譯。

還記得我昨天說過如何編譯Lua么,現(xiàn)在請你再做一遍,不同的是,請把lua++的程序包中的src/lib中的所有h和cpp,還有 include下的那個.h拷貝到你上次建立的lua工程中。然后全部添加到你的靜態(tài)鏈接庫工程中去,重新編譯。會生成一個新的lua.lib,這個 lua就自動包含了lua++的功能。最后記得把tolua++.h放在你的Include文件夾下。
行了,我們把上次CLuaFn類稍微改一下。

extern “C”
{
??????? #include “l(fā)ua.h”
??????? #include “l(fā)ualib.h”
??????? #include “l(fā)auxlib.h”
??????? #include “tolua++”?? //這里加一行
};

class CLuaFn
{
public:
??????? CLuaFn(void);
??????? ~CLuaFn(void);

??????? void Init();??????????? //初始化Lua對象指針參數(shù)
??????? void Close();???????? //關閉Lua對象指針

??????? bool LoadLuaFile(const char* pFileName);????????????????????????????? //加載指定的Lua文件
??????? bool CallFileFn(const char* pFunctionName, int nParam1, int nParam2);??????? //執(zhí)行指定Lua文件中的函數(shù)

private:
??????? lua_State* m_pState;?? //這個是Lua的State對象指針,你可以一個lua文件對應一個。
};

行了,這樣我們就能用Lua++下的功能了。
昨天,大家看到了 bool CallFileFn(const char* pFunctionName, int nParam1, int nParam2);這個函數(shù)的運用。演示了真么調用Lua函數(shù)。
下面,我改一下,這個函數(shù)。為什么?還是因為freeeyes很懶,我可不想每有一個函數(shù),我都要寫一個C++函數(shù)去調用,太累!我要寫一個通用的!支持任意函數(shù)調用的接口!
于是我創(chuàng)建了兩個類。支持任意參數(shù)的輸入和輸出,并打包送給lua去執(zhí)行,說干就干。

#ifndef _PARAMDATA_H
#define _PARAMDATA_H

#include <vector>

#define MAX_PARAM_200 200

using namespace std;

struct _ParamData
{
public:
??????? void* m_pParam;
??????? char? m_szType[MAX_PARAM_200];
??????? int?? m_TypeLen;

public:
??????? _ParamData()
??????? {
??????????????? m_pParam??? = NULL;
??????????????? m_szType[0] = ‘/0′;
??????????????? m_TypeLen?? = 0;
??????? };

??????? _ParamData(void* pParam, const char* szType, int nTypeLen)
??????? {
??????????????? SetParam(pParam, szType, nTypeLen);
??????? }

??????? ~_ParamData() {};

??????? void SetParam(void* pParam, const char* szType, int nTypeLen)
??????? {
??????????????? m_pParam = pParam;
??????????????? sprintf(m_szType, “%s”, szType);
??????????????? m_TypeLen = nTypeLen;
??????? };

??????? bool SetData(void* pParam, int nLen)
??????? {
??????????????? if(m_TypeLen < nLen)
??????????????? {
??????????????????????? return false;
??????????????? }

??????????????? if(nLen > 0)
??????????????? {
??????????????????????? memcpy(m_pParam, pParam, nLen);
??????????????? }
??????????????? else
??????????????? {
??????????????????????? memcpy(m_pParam, pParam, m_TypeLen);
??????????????? }
??????????????? return true;
??????? }

??????? void* GetParam()
??????? {
??????????????? return m_pParam;
??????? }

??????? const char* GetType()
??????? {
??????????????? return m_szType;
??????? }

??????? bool CompareType(const char* pType)
??????? {
??????????????? if(0 == strcmp(m_szType, pType))
??????????????? {
??????????????????????? return true;
??????????????? }
??????????????? else
??????????????? {
??????????????????????? return false;
??????????????? }
??????? }
};

class CParamGroup
{
public:
??????? CParamGroup() {};
??????? ~CParamGroup()
??????? {
??????????????? Close();
??????? };

??????? void Init()
??????? {
??????????????? m_vecParamData.clear();
??????? };

??????? void Close()
??????? {
??????????????? for(int i = 0; i < (int)m_vecParamData.size(); i++)
??????????????? {
??????????????????????? _ParamData* pParamData = m_vecParamData;
??????????????????????? delete pParamData;
??????????????????????? pParamData = NULL;
??????????????? }
??????????????? m_vecParamData.clear();
??????? };

??????? void Push(_ParamData* pParam)
??????? {
??????????????? if(pParam != NULL)
??????????????? {
??????????????????????? m_vecParamData.push_back(pParam);
??????????????? }
??????? };

??????? _ParamData* GetParam(int nIndex)
??????? {
??????????????? if(nIndex < (int)m_vecParamData.size())
??????????????? {
??????????????????????? return m_vecParamData[nIndex];
??????????????? }
??????????????? else
??????????????? {
??????????????????????? return NULL;
??????????????? }
??????? };

??????? int GetCount()
??????? {
??????????????? return (int)m_vecParamData.size();
??????? }

private:
??????? typedef vector<_ParamData*> vecParamData;
??????? vecParamData m_vecParamData;
};

#endif

#endif

我創(chuàng)建了兩個類,把Lua要用到的類型,數(shù)據(jù)都封裝起來了。這樣,我只需要這么改寫這個函數(shù)。
bool CallFileFn(const char* pFunctionName, CParamGroup& ParamIn, CParamGroup& ParamOut);
它就能按照不同的參數(shù)自動給我調用,嘿嘿,懶到家吧!
其實這兩個類很簡單,_ParamData是參數(shù)類,把你要用到的參數(shù)放入到這個對象中去,標明類型的大小,類型名稱,內存塊。而CParamGroup負責將很多很多的_ParamData打包在一起,放在vector里面。

好了,讓我們看看CallFileFn函數(shù)里面我怎么改的。

bool CLuaFn::CallFileFn(const char* pFunctionName, CParamGroup& ParamIn, CParamGroup& ParamOut)
{
??????? int nRet = 0;
??????? int i??? = 0;
??????? if(NULL == m_pState)
??????? {
??????????????? printf(“[CLuaFn::CallFileFn]m_pState is NULL./n”);
??????????????? return false;
??????? }

??????? lua_getglobal(m_pState, pFunctionName);

??????? //加載輸入?yún)?shù)
??????? for(i = 0; i < ParamIn.GetCount(); i++)
??????? {
??????????????? PushLuaData(m_pState, ParamIn.GetParam(i));
??????? }

??????? nRet = lua_pcall(m_pState, ParamIn.GetCount(), ParamOut.GetCount(), 0);
??????? if (nRet != 0)
??????? {
??????????????? printf(“[CLuaFn::CallFileFn]call function(%s) error(%s)./n”, pFunctionName, lua_tostring(m_pState, -1));
??????????????? return false;
??????? }

??????? //獲得輸出參數(shù)
??????? int nPos = 0;
??????? for(i = ParamOut.GetCount() – 1; i >= 0; i–)
??????? {
??????????????? nPos–;
??????????????? PopLuaData(m_pState, ParamOut.GetParam(i), nPos);
??????? }

??????? int nCount = lua_gettop(m_pState);
??????? lua_settop(m_pState, -1-ParamOut.GetCount());

??????? return true;
}

呵呵,別的沒變,加了兩個循環(huán),因為考慮lua是可以支持多結果返回的,所以我也做了一個循環(huán)接受參數(shù)。
lua_settop(m_pState, -1-ParamOut.GetCount());這句話是不是有些意思,恩,是的,我這里做了一個小技巧,因為我不知道返回參數(shù)有幾個,所以我會根據(jù)返回參數(shù)的個數(shù)重新設置棧頂。這樣做可以返回任意數(shù)量的棧而且清除干凈。
或許細心的你已經(jīng)發(fā)現(xiàn),里面多了兩個函數(shù)。恩,是的。來看看這兩個函數(shù)在干什么。

bool CLuaFn::PushLuaData(lua_State* pState, _ParamData* pParam)
{
??????? if(pParam == NULL)
??????? {
??????????????? return false;
??????? }

??????? if(pParam->CompareType(“string”))
??????? {
??????????????? lua_pushstring(m_pState, (char* )pParam->GetParam());
??????????????? return true;
??????? }

??????? if(pParam->CompareType(“int”))
??????? {
??????????????? int* nData = (int* )pParam->GetParam();
??????????????? lua_pushnumber(m_pState, *nData);
??????????????? return true;
??????? }
??????? else
??????? {
??????????????? void* pVoid = pParam->GetParam();
??????????????? tolua_pushusertype(m_pState, pVoid, pParam->GetType());
??????????????? return true;
??????? }
}

參數(shù)入棧操作,呵呵,或許你會問tolua_pushusertype(m_pState, pVoid, pParam->GetType());這句話,你可能有些看不懂,沒關系,我會在下一講詳細的解釋Lua++的一些API的用法。現(xiàn)在大概和你說 一下,這句話的意思就是,把一個C++對象傳輸給Lua函數(shù)。
再看看,下面一個。

bool CLuaFn:: PopLuaData(lua_State* pState, _ParamData* pParam, int nIndex)
{
??????? if(pParam == NULL)
??????? {
??????????????? return false;
??????? }

??????? if(pParam->CompareType(“string”))
??????? {
??????????????? if (lua_isstring(m_pState, nIndex) == 1)
??????????????? {
??????????????????????? const char* pData = (const char*)lua_tostring(m_pState, nIndex);
??????????????????????? pParam->SetData((void* )pData, (int)strlen(pData));
??????????????? }
??????????????? return true;
??????? }

??????? if(pParam->CompareType(“int”))
??????? {
??????????????? if (lua_isnumber(m_pState, nIndex) == 1)
??????????????? {
??????????????????????? int nData = (int)lua_tonumber(m_pState, nIndex);
??????????????????????? pParam->SetData(&nData, sizeof(int));
??????????????? }
??????????????? return true;
??????? }
??????? else
??????? {
??????????????? pParam->SetData(tolua_tousertype(m_pState, nIndex, NULL), -1);
??????????????? return true;
??????? }
}

彈出一個參數(shù)并賦值。pParam->SetData(tolua_tousertype(m_pState, nIndex, NULL), -1);這句話同樣,我在下一講中詳細介紹。
呵呵,好了,我們又進了一步,我們可以用這個函數(shù)綁定任意一個Lua函數(shù)格式。而代碼不用多寫,懶蛋的目的達到了。
呵呵,這一講主要是介紹了一些基本知識,或許有點多余,但是我覺得是必要的,在下一講中,我講開始詳細介紹如何綁定一個C++對象給Lua,并讓Lua對其修改。然后返回結果。休息一下,休息一下先。

Lua腳本在C++下的舞步(三)

上一講我把Lua基本的棧規(guī)則講了一下,然后完善了一下我的CLuaFn類。讓它可以支持任意參數(shù)數(shù)量和函數(shù)名稱的傳值。當然,這些功能是為了今天這篇文章而鋪路的。
看 了七貓的回帖,呵呵,確實應該說一下SWIG這個工具,說真的,我對這個工具理解不深,因為沒有怎么用過,讀過一些關于它的文章,似乎是幫你把C++的功 能封裝成一個Lua基本庫的東西,但是后來研究,他可以很輕松幫你把公用函數(shù)封裝成一個Lua的基本庫(類似C++的dll),但是對于我的需求而言,可 能不太一樣。因為我大量的是需要在C++里面進行數(shù)據(jù)傳輸以及變量的交互,所以為了緊貼C++,我需要很多關聯(lián)數(shù)據(jù)的處理。
我是一名C++程序員,所以在很多時候,不想過多的使用Lua的特性,因為個人感覺,Lua的語法要比C++的更加靈活。而我更希望,在函數(shù)調用的某些習慣上,遵循一些C++的規(guī)則。
好了,廢話少說,我們先來看一個類(頭文件)。假設我們要把這個對象,傳輸給Lua進行調用。

#ifndef _TEST_H
#define _TEST_H

class CTest
{
public:
??????? CTest(void);
??????? ~CTest(void);

??????? char* GetData();
??????? void SetData(const char* pData);

private:
??????? char m_szData[200];
};
#endif

這個類里面有兩個函數(shù),一個是GetData(),一個是SetData(),之所以這么寫,我要讓Lua不僅能使用我的類,還可以給這個類使用參數(shù)。
那么,cpp文件,我們姑且這樣寫。(當然,你可以進行修改,按照你喜歡的方式寫一個方法,呵呵)

char* CTest::GetData()
{
??????? printf(“[CTest::GetData]%s./n”, m_szData);
??????? return m_szData;
}

void CTest::SetData(const char* pData)
{
??????? sprintf(m_szData, “%s”, pData);
}

這是一個標準的類,我需要這個類在Lua里面可以創(chuàng)造出來,并賦予數(shù)值,甚至我可以把CTest作為一個Lua函數(shù)參數(shù),傳給Lua函數(shù)讓它去給我 處理。讓我們來看看怎么做。如果使用標準的Lua語法,有點多,所以我就借用一下上次提到的tolua來做到這一切,我一句句的解釋。姑且我們把這些代碼 放在LuaFn.cpp里面。

static int tolua_new_CTest(lua_State* pState)
{
??????? CTest* pTest = new CTest();
??????? tolua_pushusertype(pState, pTest, “CTest”);
??????? return 1;
}

static int tolua_delete_CTest(lua_State* pState)
{
??????? CTest* pTest = (CTest* )tolua_tousertype(pState, 1, 0);
??????? if(NULL != pTest)
??????? {
??????????????? delete pTest;
??????? }
??????? return 1;
}

static int tolua_SetData_CTest(lua_State* pState)
{
??????? CTest* pTest = (CTest* )tolua_tousertype(pState, 1, 0);
??????? const char* pData = tolua_tostring(pState, 2, 0);

??????? if(pData != NULL && pTest != NULL)
??????? {
??????????????? pTest->SetData(pData);
??????? }

??????? return 1;
}

static int tolua_GetData_CTest(lua_State* pState)
{
??????? CTest* pTest = (CTest* )tolua_tousertype(pState, 1, 0);

??????? if(pTest != NULL)
??????? {
??????????????? char* pData = pTest->GetData();
??????????????? tolua_pushstring(pState, pData);
??????? }

??????? return 1;
}

看看這幾個靜態(tài)函數(shù)在干什么。
我要在Lua里面使用CTest,必須讓Lua里這個CTest對象能夠順利的創(chuàng)造和銷毀。tolua_new_CTest()和tolua_delete_CTest()就是干這個的。
tolua_pushusertype(pState, pTest, “CTest”); 這句話的意思是,將一個已經(jīng)在Lua注冊的”CTest”對象指針,壓入數(shù)據(jù)棧。
同理,CTest* pTest = (CTest* )tolua_tousertype(pState, 1, 0);是將數(shù)據(jù)棧下的對象以(CTest* )的指針形式彈出來。
tolua_SetData_CTest() 函數(shù)和tolua_GetData_CTest分別對應CTest的SetData方法和GetData()方法。因為我們的SetData方法里面存在 變量,那么同樣,我們需要使用const char* pData = tolua_tostring(pState, 2, 0);將參數(shù)彈出來,然后輸入到pTest->SetData(pData);對象中去,當然,你可以有更多若干個參數(shù)。隨你的喜好。這里只做一個 舉例。
好了,你一定會問,這么多的靜態(tài)函數(shù),用在哪里?呵呵,當然是給Lua注冊,當你把這些數(shù)據(jù)注冊到Lua里面,你就可以輕松的在Lua中使用它們。
讓我們看看,注冊是怎么做到的。
還是在CLuaFn類里面,我們增加一個函數(shù)。比如叫做bool InitClass();

bool CLuaFn::InitClass()
{
??????? if(NULL == m_pState)
??????? {
??????????????? printf(“[CLuaFn::InitClass]m_pState is NULL./n”);
??????????????? return false;
??????? }

??????? tolua_open(m_pState);
??????? tolua_module(m_pState, NULL, 0);
??????? tolua_beginmodule(m_pState, NULL);

??????? tolua_usertype(m_pState, “CTest”);
??????? tolua_cclass(m_pState, “CTest”, “CTest”, “”, tolua_delete_CTest);

??????? tolua_beginmodule(m_pState, “CTest”);
??????? tolua_function(m_pState, “new”, tolua_new_CTest);
??????? tolua_function(m_pState, “SetData”, tolua_SetData_CTest);
??????? tolua_function(m_pState, “GetData”, tolua_GetData_CTest);
??????? tolua_endmodule(m_pState);

??????? tolua_endmodule(m_pState);

??????? return true;
}

上面的代碼,就是我把上面的幾個靜態(tài)函數(shù),綁定到Lua的基礎對象中去。
tolua_beginmodule(m_pState, “CTest”);是只注冊一個模塊,比如,我們管CTest叫做”CTest”,保持和C++的名稱一樣。這樣在Lua的對象庫中就會多了一個 CTest的對象描述,等同于string,number等等基本類型,同理,你也可以用同樣的方法,注冊你的MFC類。是不是有點明白了?這里要注 意,tolua_beginmodule()和tolua_endmodule()對象必須成對出現(xiàn),如果出現(xiàn)不成對的,你注冊的C++類型將會失敗。
tolua_function(m_pState, “SetData”, tolua_SetData_CTest);指的是將Lua里面CTest對象的”SetData”綁定到你的tolua_SetData_CTest()函數(shù)中去。

好的,讓我們來點激動人心的東西。還記得我們的Simple.lua的文件么。我們來改一下它。

function func_Add(x, y)
? local test = CTest:new();
? test:SetData(“I’m freeeyes!”);
? test:GetData();
? return x..y;
end

我在這個函數(shù)里面,New了一個CTest對象,并進行賦值操作,最后把結果打印在屏幕上。你或許會問,最后一句不是x+y么,怎么變成了 x..y,呵呵,在Lua中,..表示聯(lián)合的意思,就好比在C++里面, string strName += “freeeyes”。原來覺得x+y有點土,索性返回一個兩個字符串的聯(lián)合吧。
好了,我們已經(jīng)把我們的這個CTest類注冊到了Lua里面,讓我們來調用一下吧。修改一下Main函數(shù)。變成以下的樣子。

int _tmain(int argc, _TCHAR* argv[])
{
??????? CLuaFn LuaFn;

??????? LuaFn.InitClass();

??????? LuaFn.LoadLuaFile(“Sample.lua”);

??????? CParamGroup ParamIn;
??????? CParamGroup ParamOut;

??????? char szData1[20] = {‘/0′};
??????? sprintf(szData1, “[freeeyes]“);
??????? _ParamData* pParam1 = new _ParamData(szData1, “string”, (int)strlen(szData1));
??????? ParamIn.Push(pParam1);

??????? char szData2[20] = {‘/0′};
??????? sprintf(szData2, “[shiqiang]“);
??????? _ParamData* pParam2 = new _ParamData(szData2, “string”, (int)strlen(szData2));
??????? ParamIn.Push(pParam2);
??????? char szData3[40] = {‘/0′};
??????? _ParamData* pParam3 = new _ParamData(szData3, “string”, 40);
??????? ParamOut.Push(pParam3);

??????? LuaFn.CallFileFn(“func_Add”, ParamIn, ParamOut);

??????? char* pData = (char* )ParamOut.GetParam(0)->GetParam();
??????? printf(“[Main]Sum = %s./n”, pData);

??????? getchar();

??????? return 0;
}

如果你完全按照我的,你就可以編譯你的工程了,運行一下,看看是啥結果?

[CTest::GetData]I’m freeeyes!.
[Main]Sum = [freeeyes][shiqiang].
看看,是不是和我輸出的一樣?

呵呵,有意思吧,你已經(jīng)可以在Lua里面用C++的函數(shù)了,那么咱們再增加一點難度,比如,我有一個CTest對象,要作為一個參數(shù),傳輸給func_Add()執(zhí)行,怎么辦?
很簡單,如果你對上面的代碼仔細閱讀,你會發(fā)現(xiàn)下面的代碼一樣簡潔。為了支持剛才要說的需求,我們需要把Sample.lua再做一點修改。

function func_Add(x, y, f)
? f:SetData(“I’m freeeyes!”);
? f:GetData();
? return x..y;
end

f假設就是我們要傳入的CTest對象。我們要在Lua里面使用它。(我們的CLuaFn都不用改,把main函數(shù)稍微改一下即可,來看看怎么寫。)

// LuaSample.cpp : 定義控制臺應用程序的入口點。
//

#include “stdafx.h”
#include “LuaFn.h”

int _tmain(int argc, _TCHAR* argv[])
{
??????? CLuaFn LuaFn;

??????? LuaFn.InitClass();

??????? LuaFn.LoadLuaFile(“Sample.lua”);

??????? CParamGroup ParamIn;
??????? CParamGroup ParamOut;

??????? char szData1[20] = {‘/0′};
??????? sprintf(szData1, “[freeeyes]“);
??????? _ParamData* pParam1 = new _ParamData(szData1, “string”, (int)strlen(szData1));
??????? ParamIn.Push(pParam1);

??????? char szData2[20] = {‘/0′};
??????? sprintf(szData2, “[shiqiang]“);
??????? _ParamData* pParam2 = new _ParamData(szData2, “string”, (int)strlen(szData2));
??????? ParamIn.Push(pParam2);

??????? //只追加了這里
??????? CTest* pTest = new CTest();
??????? _ParamData* pParam3 = new _ParamData(pTest, “CTest”, sizeof(CTest));
??????? ParamIn.Push(pParam3);
?????? //追加結束
??????? char szData4[40] = {‘/0′};
??????? _ParamData* pParam4 = new _ParamData(szData4, “string”, 40);
??????? ParamOut.Push(pParam4);

??????? LuaFn.CallFileFn(“func_Add”, ParamIn, ParamOut);

??????? char* pData = (char* )ParamOut.GetParam(0)->GetParam();
??????? printf(“[Main]Sum = %s./n”, pData);

??????? getchar();

??????? return 0;
}

好了,就這么點代碼,改好了,我們再Build一下,然后點擊運行。看看輸出結果,是不是和以前的一樣?
恩,是不是有點興奮了?你成功的讓Lua開始調用你的C++對象了!并且按照你要的方式執(zhí)行!還記得我曾在第一篇文章里面許諾過,我會讓你畫出一個MFC窗體么?呵呵,如果你到現(xiàn)在依然覺得很清晰的話,說明你的距離已經(jīng)不遠了。

既然已經(jīng)到了這里,我們索性再加點難度,如果我要把CTest作為一個對象返回回來怎么做?很簡單,且看。

int _tmain(int argc, _TCHAR* argv[])
{
??????? CLuaFn LuaFn;

??????? LuaFn.InitClass();

??????? LuaFn.LoadLuaFile(“Sample.lua”);

??????? CParamGroup ParamIn;
??????? CParamGroup ParamOut;

??????? char szData1[20] = {‘/0′};
??????? sprintf(szData1, “[freeeyes]“);
??????? _ParamData* pParam1 = new _ParamData(szData1, “string”, (int)strlen(szData1));
??????? ParamIn.Push(pParam1);

??????? char szData2[20] = {‘/0′};
??????? sprintf(szData2, “[shiqiang]“);
??????? _ParamData* pParam2 = new _ParamData(szData2, “string”, (int)strlen(szData2));
??????? ParamIn.Push(pParam2);

??????? CTest* pTest = new CTest();
??????? _ParamData* pParam3 = new _ParamData(pTest, “CTest”, sizeof(CTest));
??????? ParamIn.Push(pParam3);
??????? CTest* pTestRsult = NULL;
??????? _ParamData* pParam4 = new _ParamData(pTestRsult, “CTest”, sizeof(pTestRsult));
??????? ParamOut.Push(pParam4);

??????? LuaFn.CallFileFn(“func_Add”, ParamIn, ParamOut);

??????? //接受Lua返回參數(shù)為CTest類型,并調用其中的方法。
??????? pTestRsult = (CTest* )ParamOut.GetParam(0)->GetParam();
??????? pTestRsult->GetData();

??????? getchar();

??????? return 0;
}

好,編譯,執(zhí)行。呵呵,看到了吧。

看到這里,如果你能看的明白,說明你已經(jīng)對Lua如何調用C++接口,以及C++如何調用Lua有了一定的理解。當然,我寫的這個類也不是很完善,不過做一半的Lua開發(fā),應該是夠用了。以以上的方式,你可以使用Lua駕馭你的C++代碼。
好了,咱們既然已經(jīng)說到這里了,再深一步,如果我的類是繼承的,怎么辦?呵呵,很好的問題。
比如,我的CTest繼承了一個CBase,我的CBase又繼承了一個。。。
在Lua里面,一樣簡單,我拿MFC的例子來舉例吧,想必大家更喜歡看。
比如 CCmdTarget繼承自CObject。
那么我在注冊的時候可以這么寫。

tolua_cclass(tolua_S, “CCmdTarget”,????? ”CCmdTarget”,????? ”CObject”,??????????? NULL);
這個表示CCmdTarget繼承自CObject對象。
當然,MFC里面還會有很多類型,比如常數(shù),Lua一樣能處理。
舉個例子說。

tolua_constant(tolua_S, “ES_AUTOHSCROLL”,?? ES_AUTOHSCROLL);
這樣注冊,你就可以在 Lua里面使用ES_AUTOHSCROLL這個常數(shù),它會自動綁定ES_AUTOHSCROLL這個C++常數(shù)對象。

呵呵,說了這么多,讓我們來點實際的。我給大家一個我以前寫的MFC封裝類(由于代碼太多,我變成附件給大家),你們可以調用,當然,如果你有興趣,就用我的MFC類,來做一個你喜歡的窗體吧,當然,你必須要用Lua腳本把它畫出來,作為最后的考驗,呵呵。

轉載于:https://www.cnblogs.com/artstyle/archive/2012/03/26/2417514.html

總結

以上是生活随笔為你收集整理的[转载]Lua脚本与C++交互的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。