首页 文章 精选 留言 我的

精选列表

搜索[游戏],共9236篇文章
优秀的个人博客,低调大师

如何用C++做游戏(3)

上一讲我把Lua基本的栈规则讲了一下,然后完善了一下我的CLuaFn类。让它可以支持任意参数数量和函数名称的传值。当然,这些功能是为了今天这篇文章而铺路的。 我是一名(C++)程序员,所以在很多时候,不想过多的使用Lua的特性,因为个人感觉,Lua的语法要比(C++)的更加灵活。而我更希望,在函数调用的某些习惯上,遵循一些(C++)的规则。 好了,废话少说,我们先来看一个类(头文件)。假设我们要把这个对象,传输给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 这个类里面有两个函数,一个是GetData(),一个是SetData(),之所以这么写,我要让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里面。 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,邀请码:柳猫,欢迎大家共同讨论

优秀的个人博客,低调大师

如何用C++做游戏(2)

上一节讲了一些基本的Lua应用,或许你会说,还是很简单么。呵呵,恩,是的,本来Lua就是为了让大家使用的方便快捷而设计的。如果设计的过为复杂,就不会有人使用了。 下面,我要强调一下,Lua的栈的一些概念,因为这个确实很重要,你会经常用到。熟练使用Lua,最重要的就是要时刻知道什么时候栈里面的数据是什么顺序,都是什么。如果你能熟练知道这些,实际你已经是Lua运用的高手了。 说 真的,第一次我接触栈的时候,没有把它想的很复杂,倒是看了网上很多的关于Lua的文章让我对栈的理解云里雾里,什么元表,什么User,什么局部变量, 什么全局变量位移。说的那叫一个晕。本人脑子笨,理解不了这么多,也不知道为什么很多人喜欢把Lua栈弄的七上八下,代码晦涩难懂。后来实在受不了了,去 Lua网站下载了Lua的文档,写的很清晰。Lua的栈实际上几句话足以。 当你初始化一个栈的时候,它的栈底是1,而栈顶相对位置是-1,说形 象一些,你可以把栈想象成一个环,有一个指针标记当前位置,如果-1,就是当前栈顶,如果是-2就是当前栈顶前面一个参数的位置。以此类推。 当然,你也可 以正序去取,这里要注意,对于Lua的很多API,下标是从1开始的。这个和C++有些不同。而且,在栈的下标中,正数表示绝对栈底的下标,负数表示相对 栈顶的相对地址,这个一定要有清晰的概念,否则很容易看晕了。 让我们看一些例子,加深理解。 lua_pushnumber(m_pState, 11); lua_pushnumber(m_pState, 12); int nIn = lua_gettop(m_pState); <–这里加了一行, lua_gettop()这个API是告诉你目前栈里元素的个数。 如果仅仅是Push两个参数,那么nIn的数值是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”); 如果是你,凭直觉,告诉我答案是什么? 现在公布答案,看看是不是和你想的一样。 [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); <–读取栈顶第一个相对坐标中的元素 int nData2 = lua_tonumber(m_pState, -2); <–读取栈顶第二个相对坐标中的元素 printf(“[Test]nData1 = %d, nData2 = %d./n”); 请你告诉我输出是什么? 答案是 [Test]nData1 = 12, nData2 = 11 呵呵,挺简单的吧,对了,其实就这么简单。网上其它的高阶运用,其实大部分都是对栈的位置进行调整。只要你抓住主要概念,看懂还是不难的。什么元表,什么变量,其实都一样,抓住核心,时刻知道栈里面的样子,就没有问题。 好了,回到我上一节的那个代码。 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。或许你会问,为什么会多一个?其实我第一次看到这个数字,也很诧异。但是确实是3。因为你 调用的函数名称占据了一个堆栈的位置。其实,在获取nIn那一刻,堆栈的样子是这样的(函数接口地址,参数1,参数2),函数名称也是一个变量入栈的。 而 nOut输出是1,lua_pcall()函数在调用成功之后,会自动的清空栈,然后把结果放入栈中。在获取nOut的一刻,栈内是这幅摸样(输出参数 1)。 这里就要再迁出一个更重要的概念了,Lua不是C++,对于C++程序员而言,一个函数会自动创建栈,当函数执行完毕后会自动清理 栈,Lua可不会给你这么做,对于Lua而言,它没有函数这个概念,一个栈对应一个lua_State指针,也就是说,你必须手动去清理你不用的栈,否则 会造成垃圾数据占据你的内存。 不信?那么咱们来验证一下,就拿昨天的代码吧,你用for循环调用100万次。看看nOut的输出结果。。我相信,程序执行不到100万次就会崩溃,而你的内存也会变的硕大无比。而nOut的输出也会是这样的 1,2,3,4,5,6。。。。。 原因就是,Lua不会清除你以前栈内的数据,每调用一次都会给你生成一个新的栈元素插入其中。 那么怎么解决呢?呵呵,其实,如果不考虑多线程的话,在你的函数最后退出前加一句话,就可以轻松解决这个问题。(Lua栈操作是非线程安全的!) lua_settop(m_pState, -2); 这句话的意思是什么?lua_settop()是设置栈顶的位置,我这么写,意思就是,栈顶指针目前在当前位置的-2的元素上。这样,我就实现了对栈的清除。仔细想一下,是不是这个道理呢? 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()一定要将数据完全取出后保存到你的别的变量中去,否则会因为清栈操作,导致你的程序异常,切记! 呵呵,说了这么多,主要是让大家如何写一个严谨的Lua程序,不要运行没两下就崩溃了。好了,基础栈的知识先说到这里,以后还有一些技巧的运用,到时候会给大家展示。 下面说一下,Lua的工具。(为什么要说这个呢?呵呵,因为我们下一步要用到其中的一个帮助我们的开发。) 呵呵,其实,Lua里面有很多简化开发的工具,你可以去http://www.sourceforge.net/去找一下。它们能够帮助你简化C++对象与Lua对象互转之间的代码。 这里说几个有名的,当然可能不全。 (lua tinker)如果你的系统在windows下,而且不考虑移植,那么我强烈推荐你去下载一个叫做lua tinker的小工具,整个工具非常简单,一个.h和一个.cpp。直接就可以引用到你的工程中,连独立编译都不用,这是一个韩国人写的Lua与 C++接口转换的类,十分方便,代码简洁(居家旅行,必备良药)。 它是基于模板的,所以你可以很轻松的把你的C对象绑定到Lua中。代码较长,呵呵, 有兴趣的朋友可以给我留言索要lua tinker的例子。就不贴在这里了。不过我个人不推荐这个东西,因为它在Linux下是编译不过去的。它使用了一种g不支持的模板写法,虽然有人在 尝试把它修改到Linux下编译,但据我所知,修改后效果较好的似乎还没有。不过如果你只是在 windows下,那就没什么可犹豫的,强烈推荐,你会喜欢它的。 (Luabinder)相信用过Boost库的朋友,或许对这个家伙很熟悉。它是一个很强大的Linux下Lua扩展包,帮你封装了很多Lua的复 杂操作,主要解决了绑定C++对象和Lua对象互动的关系,非常强大,不过嘛,对于freeeyes而言,还是不推荐,因为freeeyes很懒,不想为 了一个Lua还要去编译一个庞大的boost库,当然,见仁见智,如果你的程序本身就已经加载了boost,那么就应该毫不犹豫的选择它。 (lua++)呵呵,这是我最喜欢,也是我一直用到现在的库,比较前两个而言,lua++的封装性没有那么好,很多东西还是需要一点代码的,不过之 所以我喜欢,是因为它是用C写的,可以在windows下和linux下轻松转换 还记得我昨天说过如何编译Lua么,现在请你再做一遍,不同的是,请把lua的程序包中的src/lib中的所有h和cpp,还有 include下的那个.h拷贝到你上次建立的lua工程中。然后全部添加到你的静态链接库工程中去,重新编译。会生成一个新的lua.lib,这个 lua就自动包含了lua的功能。最后记得把tolua++.h放在你的Include文件夹下。 行了,我们把上次CLuaFn类稍微改一下。 extern “C” { #include “lua.h” #include “lualib.h” #include “lauxlib.h” #include “tolua++” //这里加一行 }; class CLuaFn { public: CLuaFn(void); ~CLuaFn(void); void Init(); //初始化Lua对象指针参数 void Close(); //关闭Lua对象指针 bool LoadLuaFile(const char* pFileName); //加载指定的Lua文件 bool CallFileFn(const char* pFunctionName, int nParam1, int nParam2); //执行指定Lua文件中的函数 private: lua_State* m_pState; //这个是Lua的State对象指针,你可以一个lua文件对应一个。 }; 行了,这样我们就能用Lua++下的功能了。 大家看到了 bool CallFileFn(const char* pFunctionName, int nParam1, int nParam2);这个函数的运用。演示了真么调用Lua函数。 下面,我改一下,这个函数。为什么?还是因为freeeyes很懒,我可不想每有一个函数,我都要写一个C++函数去调用,太累!我要写一个通用的!支持任意函数调用的接口! 于是我创建了两个类。支持任意参数的输入和输出,并打包送给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; 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 我创建了两个类,把Lua要用到的类型,数据都封装起来了。这样,我只需要这么改写这个函数。 bool CallFileFn(const char* pFunctionName, CParamGroup& ParamIn, CParamGroup& ParamOut); 它就能按照不同的参数自动给我调用,嘿嘿,懒到家吧! 其实这两个类很简单,_ParamData是参数类,把你要用到的参数放入到这个对象中去,标明类型的大小,类型名称,内存块。而CParamGroup负责将很多很多的_ParamData打包在一起,放在vector里面。 好了,让我们看看CallFileFn函数里面我怎么改的。 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; } 别的没变,加了两个循环,因为考虑lua是可以支持多结果返回的,所以我也做了一个循环接受参数。 lua_settop(m_pState, -1-ParamOut.GetCount());这句话是不是有些意思,恩,是的,我这里做了一个小技巧,因为我不知道返回参数有几个,所以我会根据返回参数的个数重新设置栈顶。这样做可以返回任意数量的栈而且清除干净。 或许细心的你已经发现,里面多了两个函数。恩,是的。来看看这两个函数在干什么。 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; } } 参数入栈操作,呵呵,或许你会问tolua_pushusertype(m_pState, pVoid, pParam->GetType());这句话,你可能有些看不懂,没关系,我会在下一讲详细的解释Lua++的一些API的用法。 现在大概和你说 一下,这句话的意思就是,把一个C++对象传输给Lua函数。 再看看,下面一个。 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; } } 弹出一个参数并赋值。pParam->SetData(tolua_tousertype(m_pState, nIndex, NULL), -1);这句话同样,我在下一讲中详细介绍。 好了,我们又进了一步,我们可以用这个函数绑定任意一个Lua函数格式。而代码不用多写,懒蛋的目的达到了。 这一讲主要是介绍了一些基本知识,或许有点多余,但是我觉得是必要的,在下一讲中,我讲开始详细介绍如何绑定一个C++对象给Lua,并让Lua对其修改。然后返回结果。 小伙伴们,还请持续关注更新,更多干货和资料请直接联系我,也可以加群710520381,邀请码:柳猫,欢迎大家共同讨论

优秀的个人博客,低调大师

33亿游戏背后的利益哲学

没有永远的朋友,也没有永远的敌人,只有永远的利益。Oracle(甲骨文)以33亿并购BI(商业智能系统)厂商Hyperion(海波龙)再次将这一哲学进行了演绎。虽然Oracle自身在BI领域排名也在第7位,但是Oracle对自己的BI产品却不是非常的重视,所以Oracle的数据库产品夹带了BI系统老大BO(Business Objects)的产品。通过这种强强的联合,牢牢的把握住了用户。在这种强强联合中,Oracle给了BO唯一在所有PeopleSoft和JDEdwards应用中嵌入的BO平台的特权,而BO更是针对Oracle做深度的BI产品开发。但这种强强的合作伙伴却没有架住日益增长的BI市场对Oracle的诱惑,也没有抵御住Oracle对微软SQL Server附带BI产品的担心。 复杂的BI市场关系 BI市场现在已经成为一个非常庞大的市场。在这个市场上,BO、Cognos、SAS、Hyperion等厂商都有自己独特的技术优势和市场,当然微软和Oracle也占有一席之地。不过微软进入这个市场的办法很简单,那就是在SQL Server 2005里整合BI插件,虽然不直接卖BI系统,通过整合却占领了部分BI的市场。这个招数对数据库老大Oracle也构成了一定的威胁,原有的通过与BO合作来把握用户的策略由于双方的利益不同而出现了裂痕。Oracle自己构建BI也就是迟早的事情。为此,Oracle早前收购了Sigma Dynamics公司,以增强Oracle商业智能BI套件以及Oracle Fusion中间件的功能。面对Oracle对与BO合作关系的逃离,BO自然没法自己发展数据库对抗,唯一的方式就是加强自己在BI领域的绝对优势和客户占有率,BO能做的最好方式就是收购或者合并。Oracle要做BI需要收购专业BI厂商,BO扩大客户占有率也需要收购或者并购专业BI厂商。这就使得BI的前五位厂商有了一个机遇。 资本才最有说服力 对于BI市场的前五位来说,Hyperion是比较特殊的一个。他在企业绩效管理开发上具有技术优势,但是他的战略合作伙伴是SAP(当然也有IBM),这点跟Oracle倒是竞争对手。基于竞争的角度,对企业绩效管理的看好以及对整合BI进入自己产品系列的决心,Oracle对Hyperion是势在必得。虽然此前BO与Hyperion已经在并购上经历了漫长的磋商,但是资本的诱惑,33亿美元的真金白银还是说服了Hyperion,BO只得败下阵来。Oracle满意的将Hyperion收入囊中,Hyperion也高兴的以超出自身市价21%的价码满意而归,而BO也只能感叹资本的不足。BO在收购后发表的声明颇有意味。 “Business Objects公司一直位居国际商务智能市场的领导地位。如今排名第七位的公司Oracle并购了市场排名第四位的企业Hyperion丝毫不会改变Business Objects的领导地位。对于Oracle来讲,BI不过是数据库或应用软件的附属产品,而客户真正需要的是能够处理关键任务的BI,以帮助他们掌握对任何厂商提供的数据库以及各种应用的深刻洞察,因为他们工作在一个兼收并蓄的异构环境之中。Business Objects公司是唯一能够提供企业级BI解决方案的公司,与此同时这些产品方案具有开放性(就数据库、中间件、应用等产品而言)、广泛性(含盖了几乎全部对BI的需求,从数据质量管理到绩效管理) 以及整合性(统一在单一的平台—BusinessObjects XI之上)等特点。” 这个意味表明了BO与Oracle的合作关系将可能走向尽头,甚至BO会投靠到Oracle敌人的阵营。如果真的是这样,相信SAP与IBM乐于成为BO新的合作伙伴,虽然SAP与IBM都有自己比较成形的BI系统。 BI似乎遥远的市场 BI不仅对于老百姓,且对于IT人士也是一个非常陌生的名词。而33亿美元相当于250多亿人民币,这如果发生在中国那也是惊天的大事,至少是分众收购好耶的15倍,但在国外却不是太大的事情。中国市场在全球IT市场份额由此可见一斑。我们的信息化水平现在在很低的水平,应用水平就更低了。甚至我们熟知的IT网站用到了自动化业务管理系统的都少之又少。希望国内的厂商正视自己的差距,注重自身的技术含量,没有自己的独有技术,资本一进入就只有被淘汰。 本文转自驳客:站在真理的对面博客51CTO博客,原文链接http://blog.51cto.com/fangsheng111/19363如需转载请自行联系原作者 驳客

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册