上一讲我把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,邀请码:柳猫,欢迎你们共同讨论