在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
前两篇文章中介绍了C++调用lua、lua栈操作的一些相关知识。 下面说一下Lua的工具。我们下一步要用到其中的一个帮助我们的开发,其实,Lua里面有很多简化开发的工具,你可以去www.sourceforge.net去找一下。它们能够帮助你简化C++对象与Lua对象互转之间的代码。这里说几个有名的,当然可能不全。
还记得我昨天说过如何编译Lua么,现在请你再做一遍,不同的是,请把lua++的程序包中的src/lib中的所有h和cpp,还有include下的那个.h拷贝到你上次建立的lua工程中。最后记得把tolua++.h放在你的Include文件夹下。下图为整个项目的目录结构(最终运行成功的目录结构): 行了,我们把上次CLuaFn类稍微改一下。 extern "C" { #include "lua.h" #include "lualib.h" #include "lauxlib.h" #include "tolua++.h" //这里加一行 }; #include "_ParamData.h" #include "CTest.h" class CLuaFn { public: CLuaFn(void); ~CLuaFn(void); void Init(); //初始化Lua对象指针参数 void Close(); //关闭Lua对象指针 bool LoadLuaFile(const char* pFileName); //加载指定的Lua文件 bool CLuaFn::CallFileFn(const char* pFunctionName, CParamGroup& ParamIn, CParamGroup& ParamOut); //执行指定Lua文件中的函数 //参数入栈 bool CLuaFn::PushLuaData(lua_State* pState, _ParamData* pParam); //参数出栈 bool CLuaFn:: PopLuaData(lua_State* pState, _ParamData* pParam, int nIndex); //新对象的创建、销毁 static int tolua_new_CTest(lua_State* pState); static int tolua_delete_CTest(lua_State* pState) ; static int tolua_SetData_CTest(lua_State* pState); static int tolua_GetData_CTest(lua_State* pState); bool InitClass(); private: lua_State* m_pState; //这个是Lua的State对象指针,你可以一个Lua文件对应一个 }; 行了,这样我们就能用Lua++下的功能了。 //支持任意参数的输入和输出,并打包送给lua去执行 #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[i]; 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 我创建了两个类,把Lua要用到的类型,数据都封装起来了。这样,我只需要这么改写这个函数。 #include "CLuaFn.h" CLuaFn::CLuaFn(void){m_pState = NULL;}; //这句干嘛 CLuaFn::~CLuaFn(void){}; //初始化函数 void CLuaFn::Init() { if (NULL == m_pState) { m_pState = lua_open(); //返回一个lua对象指针 luaL_openlibs(m_pState); //加载了所有你可能用到的Lua基本库 } } //关闭Lua对象并释放资源 void CLuaFn::Close() { if(NULL != m_pState) { lua_close(m_pState); m_pState = NULL; } } 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; } //参数入栈 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; } } // 参数出栈 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; } } //调用函数 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); //加载输入参数 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; } //获得输出参数 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; } /* ************************************************************* */ int CLuaFn::tolua_new_CTest(lua_State* pState) { CTest* pTest = new CTest(); //将一个已经在Lua注册的"CTest"对象指针,压入数据栈 tolua_pushusertype(pState, pTest, "CTest"); return 1; } int CLuaFn::tolua_delete_CTest(lua_State* pState) { //将数据栈下的对象以(CTest* )的指针形式弹出来 CTest* pTest = (CTest* )tolua_tousertype(pState, 1, 0); if(NULL != pTest) { delete pTest; } return 1; } /*tolua_SetData_CTest()函数和tolua_GetData_CTest分别对应CTest的SetData方法和GetData()方法。 因为我们的SetData方法里面存在变量,那么同样,我们需要使用const char* pData = tolua_tostring(pState, 2, 0);将参数弹出来, 然后输入到pTest->SetData(pData);对象中去,当然,你可以有更多若干个参数*/ int CLuaFn::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; } int CLuaFn::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; } /* ************************************************************* */ 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); //只注册一个模块,比如,我们管CTest叫做"CTest",保持和C++的名称一样。这样在Lua的对象库中就会多了一个CTest的对象描述,等同于string,number等等基本类型 tolua_beginmodule(m_pState, "CTest"); tolua_function(m_pState, "new", tolua_new_CTest); //将Lua里面CTest对象的"SetData"绑定到你的tolua_SetData_CTest()函数中去 tolua_function(m_pState, "SetData", tolua_SetData_CTest); tolua_function(m_pState, "GetData", tolua_GetData_CTest); //tolua_beginmodule()和tolua_endmodule()对象必须成对出现,如果出现不成对的,你注册的C++类型将会失败 tolua_endmodule(m_pState); tolua_endmodule(m_pState); return true; } 下面来看一个类(头文件)。假设我们要把这个对象,传输给Lua进行调用。 #ifndef _TEST_H #define _TEST_H #include<stdio.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里面可以创造出来,并赋予数值, 甚至我可以把CTest作为一个Lua函数参数,传给Lua函数让它去给我处理*/ #include "CTest.h" CTest::CTest(void){}; CTest::~CTest(void){}; 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里面。 /* ************************************************************* */ int CLuaFn::tolua_new_CTest(lua_State* pState) { CTest* pTest = new CTest(); //将一个已经在Lua注册的"CTest"对象指针,压入数据栈 tolua_pushusertype(pState, pTest, "CTest"); return 1; } int CLuaFn::tolua_delete_CTest(lua_State* pState) { //将数据栈下的对象以(CTest* )的指针形式弹出来 CTest* pTest = (CTest* )tolua_tousertype(pState, 1, 0); if(NULL != pTest) { delete pTest; } return 1; } /*tolua_SetData_CTest()函数和tolua_GetData_CTest分别对应CTest的SetData方法和GetData()方法。 因为我们的SetData方法里面存在变量,那么同样,我们需要使用const char* pData = tolua_tostring(pState, 2, 0);将参数弹出来, 然后输入到pTest->SetData(pData);对象中去,当然,你可以有更多若干个参数*/ int CLuaFn::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; } int CLuaFn::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; } /* ************************************************************* */ 看看这几个静态函数在干什么。 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); //只注册一个模块,比如,我们管CTest叫做"CTest",保持和C++的名称一样。这样在Lua的对象库中就会多了一个CTest的对象描述,等同于string,number等等基本类型 tolua_beginmodule(m_pState, "CTest"); tolua_function(m_pState, "new", tolua_new_CTest); //将Lua里面CTest对象的"SetData"绑定到你的tolua_SetData_CTest()函数中去 tolua_function(m_pState, "SetData", tolua_SetData_CTest); tolua_function(m_pState, "GetData", tolua_GetData_CTest); //tolua_beginmodule()和tolua_endmodule()对象必须成对出现,如果出现不成对的,你注册的C++类型将会失败 tolua_endmodule(m_pState); tolua_endmodule(m_pState); return true; } 上面的代码,就是我把上面的几个静态函数,绑定到Lua的基础对象中去。 function func_Add(x, y) -- New了一个CTest对象,并进行赋值操作,最后把结果打印在屏幕上 local test = CTest:new() test:SetData("I'm xiaotian") test:GetData() return x..y; end 我在这个函数里面,New了一个CTest对象,并进行赋值操作,最后把结果打印在屏幕上。你或许会问,最后一句不是x+y么,怎么变成了x..y,呵呵,在Lua中,..表示联合的意思,就好比在C++里面, string strName += "freeeyes"。原来觉得x+y有点土,索性返回一个两个字符串的联合吧。 #include "CLuaFn.h" int main(int argc, char* argv[]) { CLuaFn CLuaFn; CLuaFn.Init(); CLuaFn.InitClass(); CLuaFn.LoadLuaFile("Sample.lua"); CParamGroup ParamIn; CParamGroup ParamOut; char szData1[20] = {'\0'}; sprintf(szData1, "[yang]"); _ParamData* pParam1 = new _ParamData(szData1, "string", (int)strlen(szData1)); ParamIn.Push(pParam1); char szData2[20] = {'\0'}; sprintf(szData2, "[xiaotian]"); _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); CLuaFn.CallFileFn("func_Add", ParamIn, ParamOut); char* pData = (char* )ParamOut.GetParam(0)->GetParam(); printf("[Main]Sum = %s.\n", pData); getchar(); return 0; } 运行一下:
|
请发表评论