上一講我把Lua基本的棧規則講了一下,而後完善了一下個人CLuaFn類。讓它能夠支持任意參數數量和函數名稱的傳值。固然,這些功能是爲了今天這篇文章而鋪路的。程序員
我是一名(C++)程序員,因此在不少時候,不想過多的使用Lua的特性,由於我的感受,Lua的語法要比(C++)的更加靈活。而我更但願,在函數調用的某些習慣上,遵循一些(C++)的規則。函數
好了,廢話少說,咱們先來看一個類(頭文件)。假設咱們要把這個對象,傳輸給Lua進行調用。ui
#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
這個類裏面有兩個函數,一個是GetData(),一個是SetData(),之因此這麼寫,我要讓Lua不只能使用個人類,還能夠給這個類使用參數。lua
那麼,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裏面能夠創造出來,並賦予數值,甚至我能夠把CTest做爲一個Lua函數參數,傳給Lua函數讓它去給我 處理。讓咱們來看看怎麼作。若是使用標準的Lua語法,有點多,因此我就借用一下上次提到的tolua來作到這一切,我一句句的解釋。姑且咱們把這些代碼 放在LuaFn.cpp裏面。code
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; }
看看這幾個靜態函數在幹什麼。對象
我要在Lua裏面使用CTest,必須讓Lua裏這個CTest對象可以順利的創造和銷燬。tolua_new_CTest()和tolua_delete_CTest()就是幹這個的。繼承
tolua_pushusertype(pState, pTest, 「CTest」);
這句話的意思是,將一個已經在Lua註冊的」CTest」對象指針,壓入數據棧,用指針形式彈出來。 。接口
,CTest* pTest = (CTest* )tolua_tousertype(pState, 1, 0);是將數據棧下的對象以(CTest* )
tolua_SetData_CTest() 函數和tolua_GetData_CTest分別對應CTest的SetData方法和GetData()方法。開發
由於咱們的SetData方法裏面存在 變量,那麼一樣,咱們須要使用const char* pData = tolua_tostring(pState, 2, 0);將參數彈出來,而後輸入到pTest->SetData(pData);對象中去,固然,你能夠有更多若干個參數。隨你的喜愛。這裏只作一個 舉例。
好了,你必定會問,這麼多的靜態函數,用在哪裏?呵呵,固然是給Lua註冊,當你把這些數據註冊到Lua裏面,你就能夠輕鬆的在Lua中使用它們。
讓咱們看看,註冊是怎麼作到的。
仍是在CLuaFn類裏面,咱們增長一個函數。好比叫作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; }
上面的代碼,就是我把上面的幾個靜態函數,綁定到Lua的基礎對象中去。
tolua_beginmodule(m_pState, 「CTest」);是隻註冊一個模塊,好比,咱們管CTest叫作」CTest」,保持和C的名稱同樣。這樣在Lua的對象庫中就會多了一個 CTest的對象描述,等同於string,number等等基本類型,同理,你也能夠用一樣的方法,註冊你的MFC類。是否是有點明白了?這裏要注 意,tolua_beginmodule()和tolua_endmodule()對象必須成對出現,若是出現不成對的,你註冊的C類型將會失敗。
tolua_function(m_pState, 「SetData」, tolua_SetData_CTest);
指的是將Lua裏面CTest對象的」SetData」綁定到你的tolua_SetData_CTest()函數中去。
好的,讓咱們來點激動人心的東西。還記得咱們的Simple.lua的文件麼。咱們來改一下它。
function func_Add(x, y) local test = CTest:new(); test:SetData(「I’m freeeyes!」); test:GetData(); return x..y; end
我在這個函數裏面,New了一個CTest對象,並進行賦值操做,最後把結果打印在屏幕上。
你或許會問,最後一句不是x+y麼,怎麼變成了 x..y,呵呵,在Lua中,..表示聯合的意思,就比如在C++裏面, string strName += 「freeeyes」。原來以爲x+y有點土,索性返回一個兩個字符串的聯合吧。
好了,咱們已經把咱們的這個CTest類註冊到了Lua裏面,讓咱們來調用一下吧。修改一下Main函數。變成如下的樣子。
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].
看看,是否是和我輸出的同樣?
呵呵,有意思吧,你已經能夠在Lua裏面用C++的函數了,那麼我們再增長一點難度,好比,我有一個CTest對象,要做爲一個參數,傳輸給func_Add()執行,怎麼辦?
很簡單,若是你對上面的代碼仔細閱讀,你會發現下面的代碼同樣簡潔。爲了支持剛纔要說的需求,咱們須要把Sample.lua再作一點修改。
function func_Add(x, y, f) f:SetData(「I’m freeeyes!」); f:GetData(); return x..y; end
f假設就是咱們要傳入的CTest對象。咱們要在Lua裏面使用它。(咱們的CLuaFn都不用改,把main函數稍微改一下便可,來看看怎麼寫。)
// 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++對象了!而且按照你要的方式執行!還記得我曾在第一篇文章裏面許諾過,我會讓你畫出一個MFC窗體麼?呵呵,若是你到如今依然以爲很清晰的話,說明你的距離已經不遠了。
既然已經到了這裏,咱們索性再加點難度,若是我要把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返回參數爲CTest類型,並調用其中的方法。 pTestRsult = (CTest* )ParamOut.GetParam(0)->GetParam(); pTestRsult->GetData(); getchar(); return 0; }
好,編譯,執行。呵呵,看到了吧。
看到這裏,若是你能看的明白,說明你已經對Lua如何調用C接口,以及C如何調用Lua有了必定的理解。固然,我寫的這個類也不是很完善,不過作一半的Lua開發,應該是夠用了。以以上的方式,你可使用Lua駕馭你的C++代碼。
好了,我們既然已經說到這裏了,再深一步,若是個人類是繼承的,怎麼辦?
好比,個人CTest繼承了一個CBase,個人CBase又繼承了一個。。。
在Lua裏面,同樣簡單,我拿MFC的例子來舉例吧,想必你們更喜歡看。
好比 CCmdTarget繼承自CObject。
那麼我在註冊的時候能夠這麼寫。
tolua_cclass(tolua_S, 「CCmdTarget」, 」CCmdTarget」, 」CObject」, NULL);
這個表示CCmdTarget繼承自CObject對象。
固然,MFC裏面還會有不少類型,好比常數,Lua同樣能處理。
舉個例子說。
tolua_constant(tolua_S, 「ES_AUTOHSCROLL」, ES_AUTOHSCROLL);
這樣註冊,你就能夠在 Lua裏面使用ES_AUTOHSCROLL這個常數,它會自動綁定ES_AUTOHSCROLL這個(C++)常數對象。
小夥伴們,還請持續關注更新,更多幹貨和資料請直接聯繫我,也能夠加羣710520381,邀請碼:柳貓,歡迎你們共同討論