Lua脚本在C++下的舞步(二

上一节讲了一些基本的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
呵呵,那么,如果我把代码换成

  1. lua_pushnumber(m_pState, 11);  
  2.   
  3. lua_pushnumber(m_pState, 12);  
  4.   
  5.   
  6.   
  7. int nIn = lua_gettop(m_pState)  
  8.   
  9.   
  10.   
  11. int nData1 = lua_tonumber(m_pState, -1);     <--读取栈顶第一个相对坐标中的元素  
  12.   
  13. int nData2 = lua_tonumber(m_pState, -2);     <--读取栈顶第二个相对坐标中的元素  
  14.   
  15. printf("[Test]nData1  = %d, nData2  = %d./n");  

请你告诉我输出是什么?
答案是
[Test]nData1  = 12, nData2  = 11
呵呵,挺简单的吧,对了,其实就这么简单。网上其它的高阶运用,其实大部分都是对栈的位置进行调整。只要你抓住主要概念,看懂还是不难的。什么元表,什么变量,其实都一样,抓住核心,时刻知道栈里面的样子,就没有问题。
好了,回到我上一节的那个代码。

  1. bool CLuaFn::CallFileFn(const char* pFunctionName, int nParam1, int nParam2)  
  2.   
  3. {  
  4.   
  5.         int nRet = 0;  
  6.   
  7.         if(NULL == m_pState)  
  8.   
  9.         {  
  10.   
  11.                 printf("[CLuaFn::CallFileFn]m_pState is NULL./n");  
  12.   
  13.                 return false;  
  14.   
  15.         }  
  16.   
  17.   
  18.   
  19.         lua_getglobal(m_pState, pFunctionName);  
  20.   
  21.   
  22.   
  23.         lua_pushnumber(m_pState, nParam1);  
  24.   
  25.         lua_pushnumber(m_pState, nParam2);  
  26.   
  27.   
  28.   
  29.         int nIn = lua_gettop(m_pState); <--在这里加一行。  
  30.   
  31.   
  32.   
  33.         nRet = lua_pcall(m_pState, 2, 1, 0);  
  34.   
  35.         if (nRet != 0)  
  36.   
  37.         {  
  38.   
  39.                 printf("[CLuaFn::CallFileFn]call function(%s) error(%d)./n", pFunctionName, nRet);  
  40.   
  41.                 return false;  
  42.   
  43.         }  
  44.   
  45.   
  46.   
  47.         if (lua_isnumber(m_pState, -1) == 1)  
  48.   
  49.         {  
  50.   
  51.                 int nSum = lua_tonumber(m_pState, -1);  
  52.   
  53.                 printf("[CLuaFn::CallFileFn]Sum = %d./n", nSum);  
  54.   
  55.         }  
  56.   
  57.   
  58.   
  59.         int nOut = lua_gettop(m_pState); <--在这里加一行。  
  60.   
  61.   
  62.   
  63.         return true;  
  64.   
  65. }  

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的元素上。这样,我就实现了对栈的清除。仔细想一下,是不是这个道理呢?

  1. bool CLuaFn::CallFileFn(const char* pFunctionName, int nParam1, int nParam2)  
  2.   
  3. {  
  4.   
  5.         int nRet = 0;  
  6.   
  7.         if(NULL == m_pState)  
  8.   
  9.         {  
  10.   
  11.                 printf("[CLuaFn::CallFileFn]m_pState is NULL./n");  
  12.   
  13.                 return false;  
  14.   
  15.         }  
  16.   
  17.   
  18.   
  19.         lua_getglobal(m_pState, pFunctionName);  
  20.   
  21.   
  22.   
  23.         lua_pushnumber(m_pState, nParam1);  
  24.   
  25.         lua_pushnumber(m_pState, nParam2);  
  26.   
  27.   
  28.   
  29.         int nIn = lua_gettop(m_pState); <--在这里加一行。  
  30.   
  31.   
  32.   
  33.         nRet = lua_pcall(m_pState, 2, 1, 0);  
  34.   
  35.         if (nRet != 0)  
  36.   
  37.         {  
  38.   
  39.                 printf("[CLuaFn::CallFileFn]call function(%s) error(%d)./n", pFunctionName, nRet);  
  40.   
  41.                 return false;  
  42.   
  43.         }  
  44.   
  45.   
  46.   
  47.         if (lua_isnumber(m_pState, -1) == 1)  
  48.   
  49.         {  
  50.   
  51.                 int nSum = lua_tonumber(m_pState, -1);  
  52.   
  53.                 printf("[CLuaFn::CallFileFn]Sum = %d./n", nSum);  
  54.   
  55.         }  
  56.   
  57.   
  58.   
  59.         int nOut = lua_gettop(m_pState); <--在这里加一行。  
  60.   
  61.         lua_settop(m_pState, -2);             <--清除不用的栈。  
  62.   
  63.   
  64.   
  65.         return true;  
  66.   
  67. }  

好了,再让我们运行100万次,看看你的程序内存,看看你的程序还崩溃不?
如果你想打印 nOut的话,输出会变成1,1,1,1,1。。。。
最后说一句,lua_tonumber()或lua_tostring()还有以后我们要用到的lua_touserdata()一定要将数据完全取出后保存到你的别的变量中去,否则会因为清栈操作,导致你的程序异常,切记!

呵呵,说了这么多,主要是让大家如何写一个严谨的Lua程序,不要运行没两下就崩溃了。好了,基础栈的知识先说到这里,以后还有一些技巧的运用,到时候会给大家展示。
下面说一下,Lua的工具。(为什么要说这个呢?呵呵,因为我们下一步要用到其中的一个帮助我们的开发。)
呵呵,其实,Lua里面有很多简化开发的工具,你可以去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++作为我们继续进行下去的垫脚石吧。
说到Lua++(http://www.codenix.com/~tolua/),这个东西还是挺有渊源的,请你先下载一个。我教你怎么编译。

还记得我昨天说过如何编译Lua么,现在请你再做一遍,不同的是,请把lua++的程序包中的src/lib中的所有h和cpp,还有include下的那个.h拷贝到你上次建立的lua工程中。然后全部添加到你的静态链接库工程中去,重新编译。会生成一个新的lua.lib,这个lua就自动包含了lua++的功能。最后记得把tolua++.h放在你的Include文件夹下。
行了,我们把上次CLuaFn类稍微改一下。

  1. extern "C"   
  2.   
  3. {  
  4.   
  5.         #include "lua.h"  
  6.   
  7.         #include "lualib.h"  
  8.   
  9.         #include "lauxlib.h"  
  10.   
  11.         #include "tolua++"   //这里加一行  
  12.   
  13. };  
  14.   
  15.   
  16.   
  17. class CLuaFn  
  18.   
  19. {  
  20.   
  21. public:  
  22.   
  23.         CLuaFn(void);  
  24.   
  25.         ~CLuaFn(void);  
  26.   
  27.   
  28.   
  29.         void Init();            //初始化Lua对象指针参数  
  30.   
  31.         void Close();         //关闭Lua对象指针  
  32.   
  33.   
  34.   
  35.         bool LoadLuaFile(const char* pFileName);                              //加载指定的Lua文件  
  36.   
  37.         bool CallFileFn(const char* pFunctionName, int nParam1, int nParam2);        //执行指定Lua文件中的函数  
  38.   
  39.   
  40.   
  41. private:  
  42.   
  43.         lua_State* m_pState;   //这个是Lua的State对象指针,你可以一个lua文件对应一个。  
  44.   
  45. };  

行了,这样我们就能用Lua++下的功能了。
昨天,大家看到了 bool CallFileFn(const char* pFunctionName, int nParam1, int nParam2);这个函数的运用。演示了真么调用Lua函数。
下面,我改一下,这个函数。为什么?还是因为freeeyes很懒,我可不想每有一个函数,我都要写一个C++函数去调用,太累!我要写一个通用的!支持任意函数调用的接口!
于是我创建了两个类。支持任意参数的输入和输出,并打包送给lua去执行,说干就干。

  1. #ifndef _PARAMDATA_H  
  2.   
  3. #define _PARAMDATA_H  
  4.   
  5.   
  6.   
  7. #include <vector>  
  8.   
  9.   
  10.   
  11. #define MAX_PARAM_200 200  
  12.   
  13.   
  14.   
  15. using namespace std;  
  16.   
  17.   
  18.   
  19. struct _ParamData  
  20.   
  21. {  
  22.   
  23. public:  
  24.   
  25.         void* m_pParam;  
  26.   
  27.         char  m_szType[MAX_PARAM_200];  
  28.   
  29.         int   m_TypeLen;  
  30.   
  31.   
  32.   
  33. public:  
  34.   
  35.         _ParamData()  
  36.   
  37.         {  
  38.   
  39.                 m_pParam    = NULL;  
  40.   
  41.                 m_szType[0] = '/0';  
  42.   
  43.                 m_TypeLen   = 0;  
  44.   
  45.         };  
  46.   
  47.   
  48.   
  49.         _ParamData(void* pParam, const char* szType, int nTypeLen)  
  50.   
  51.         {  
  52.   
  53.                 SetParam(pParam, szType, nTypeLen);  
  54.   
  55.         }  
  56.   
  57.   
  58.   
  59.         ~_ParamData() {};  
  60.   
  61.   
  62.   
  63.         void SetParam(void* pParam, const char* szType, int nTypeLen)  
  64.   
  65.         {  
  66.   
  67.                 m_pParam = pParam;  
  68.   
  69.                 sprintf(m_szType, "%s", szType);  
  70.   
  71.                 m_TypeLen = nTypeLen;  
  72.   
  73.         };  
  74.   
  75.   
  76.   
  77.         bool SetData(void* pParam, int nLen)  
  78.   
  79.         {  
  80.   
  81.                 if(m_TypeLen < nLen)  
  82.   
  83.                 {  
  84.   
  85.                         return false;  
  86.   
  87.                 }  
  88.   
  89.   
  90.   
  91.                 if(nLen > 0)  
  92.   
  93.                 {  
  94.   
  95.                         memcpy(m_pParam, pParam, nLen);  
  96.   
  97.                 }  
  98.   
  99.                 else  
  100.   
  101.                 {  
  102.   
  103.                         memcpy(m_pParam, pParam, m_TypeLen);  
  104.   
  105.                 }  
  106.   
  107.                 return true;  
  108.   
  109.         }  
  110.   
  111.   
  112.   
  113.         void* GetParam()  
  114.   
  115.         {  
  116.   
  117.                 return m_pParam;  
  118.   
  119.         }  
  120.   
  121.   
  122.   
  123.         const char* GetType()  
  124.   
  125.         {  
  126.   
  127.                 return m_szType;  
  128.   
  129.         }  
  130.   
  131.   
  132.   
  133.         bool CompareType(const char* pType)  
  134.   
  135.         {  
  136.   
  137.                 if(0 == strcmp(m_szType, pType))  
  138.   
  139.                 {  
  140.   
  141.                         return true;  
  142.   
  143.                 }  
  144.   
  145.                 else  
  146.   
  147.                 {  
  148.   
  149.                         return false;  
  150.   
  151.                 }  
  152.   
  153.         }  
  154.   
  155. };  
  156.   
  157.   
  158.   
  159.   
  160.   
  161. class CParamGroup  
  162.   
  163. {  
  164.   
  165. public:  
  166.   
  167.         CParamGroup() {};  
  168.   
  169.         ~CParamGroup()  
  170.   
  171.         {   
  172.   
  173.                 Close();   
  174.   
  175.         };  
  176.   
  177.   
  178.   
  179.         void Init()  
  180.   
  181.         {  
  182.   
  183.                 m_vecParamData.clear();  
  184.   
  185.         };  
  186.   
  187.   
  188.   
  189.         void Close()  
  190.   
  191.         {  
  192.   
  193.                 for(int i = 0; i < (int)m_vecParamData.size(); i++)  
  194.   
  195.                 {  
  196.   
  197.                         _ParamData* pParamData = m_vecParamData;  
  198.   
  199.                         delete pParamData;  
  200.   
  201.                         pParamData = NULL;  
  202.   
  203.                 }  
  204.   
  205.                 m_vecParamData.clear();  
  206.   
  207.         };  
  208.   
  209.   
  210.   
  211.         void Push(_ParamData* pParam)  
  212.   
  213.         {  
  214.   
  215.                 if(pParam != NULL)  
  216.   
  217.                 {  
  218.   
  219.                         m_vecParamData.push_back(pParam);  
  220.   
  221.                 }  
  222.   
  223.         };  
  224.   
  225.   
  226.   
  227.         _ParamData* GetParam(int nIndex)  
  228.   
  229.         {  
  230.   
  231.                 if(nIndex < (int)m_vecParamData.size())  
  232.   
  233.                 {  
  234.   
  235.                         return m_vecParamData[nIndex];  
  236.   
  237.                 }  
  238.   
  239.                 else  
  240.   
  241.                 {  
  242.   
  243.                         return NULL;  
  244.   
  245.                 }  
  246.   
  247.         };  
  248.   
  249.   
  250.   
  251.         int GetCount()  
  252.   
  253.         {  
  254.   
  255.                 return (int)m_vecParamData.size();  
  256.   
  257.         }  
  258.   
  259.   
  260.   
  261. private:  
  262.   
  263.         typedef vector<_ParamData*> vecParamData;  
  264.   
  265.         vecParamData m_vecParamData;  
  266.   
  267. };  
  268.   
  269.   
  270.   
  271.   
  272.   
  273. #endif  
  274.   
  275.   
  276.   
  277.   
  278.   
  279. #endif  
  

我创建了两个类,把Lua要用到的类型,数据都封装起来了。这样,我只需要这么改写这个函数。
bool CallFileFn(const char* pFunctionName, CParamGroup& ParamIn, CParamGroup& ParamOut);
它就能按照不同的参数自动给我调用,嘿嘿,懒到家吧!
其实这两个类很简单,_ParamData是参数类,把你要用到的参数放入到这个对象中去,标明类型的大小,类型名称,内存块。而CParamGroup负责将很多很多的_ParamData打包在一起,放在vector里面。

好了,让我们看看CallFileFn函数里面我怎么改的。

  1. bool CLuaFn::CallFileFn(const char* pFunctionName, CParamGroup& ParamIn, CParamGroup& ParamOut)  
  2.   
  3. {  
  4.   
  5.         int nRet = 0;  
  6.   
  7.         int i    = 0;  
  8.   
  9.         if(NULL == m_pState)  
  10.   
  11.         {  
  12.   
  13.                 printf("[CLuaFn::CallFileFn]m_pState is NULL./n");  
  14.   
  15.                 return false;  
  16.   
  17.         }  
  18.   
  19.   
  20.   
  21.         lua_getglobal(m_pState, pFunctionName);  
  22.   
  23.   
  24.   
  25.         //加载输入参数  
  26.   
  27.         for(i = 0; i < ParamIn.GetCount(); i++)  
  28.   
  29.         {  
  30.   
  31.                 PushLuaData(m_pState, ParamIn.GetParam(i));  
  32.   
  33.         }  
  34.   
  35.   
  36.   
  37.         nRet = lua_pcall(m_pState, ParamIn.GetCount(), ParamOut.GetCount(), 0);  
  38.   
  39.         if (nRet != 0)  
  40.   
  41.         {  
  42.   
  43.                 printf("[CLuaFn::CallFileFn]call function(%s) error(%s)./n", pFunctionName, lua_tostring(m_pState, -1));  
  44.   
  45.                 return false;  
  46.   
  47.         }  
  48.   
  49.   
  50.   
  51.         //获得输出参数  
  52.   
  53.         int nPos = 0;  
  54.   
  55.         for(i = ParamOut.GetCount() - 1; i >= 0; i--)  
  56.   
  57.         {  
  58.   
  59.                 nPos--;  
  60.   
  61.                 PopLuaData(m_pState, ParamOut.GetParam(i), nPos);  
  62.   
  63.         }  
  64.   
  65.   
  66.   
  67.         int nCount = lua_gettop(m_pState);  
  68.   
  69.         lua_settop(m_pState, -1-ParamOut.GetCount());  
  70.   
  71.   
  72.   
  73.         return true;  
  74.   
  75. }  

呵呵,别的没变,加了两个循环,因为考虑lua是可以支持多结果返回的,所以我也做了一个循环接受参数。
lua_settop(m_pState, -1-ParamOut.GetCount());这句话是不是有些意思,恩,是的,我这里做了一个小技巧,因为我不知道返回参数有几个,所以我会根据返回参数的个数重新设置栈顶。这样做可以返回任意数量的栈而且清除干净。
或许细心的你已经发现,里面多了两个函数。恩,是的。来看看这两个函数在干什么。

  1. bool CLuaFn::PushLuaData(lua_State* pState, _ParamData* pParam)  
  2.   
  3. {  
  4.   
  5.         if(pParam == NULL)  
  6.   
  7.         {  
  8.   
  9.                 return false;  
  10.   
  11.         }  
  12.   
  13.   
  14.   
  15.         if(pParam->CompareType("string"))  
  16.   
  17.         {  
  18.   
  19.                 lua_pushstring(m_pState, (char* )pParam->GetParam());  
  20.   
  21.                 return true;  
  22.   
  23.         }  
  24.   
  25.   
  26.   
  27.         if(pParam->CompareType("int"))  
  28.   
  29.         {  
  30.   
  31.                 int* nData = (int* )pParam->GetParam();  
  32.   
  33.                 lua_pushnumber(m_pState, *nData);  
  34.   
  35.                 return true;  
  36.   
  37.         }  
  38.   
  39.         else  
  40.   
  41.         {  
  42.   
  43.                 void* pVoid = pParam->GetParam();  
  44.   
  45.                 tolua_pushusertype(m_pState, pVoid, pParam->GetType());  
  46.   
  47.                 return true;  
  48.   
  49.         }  
  50.   
  51. }  

参数入栈操作,呵呵,或许你会问tolua_pushusertype(m_pState, pVoid, pParam->GetType());这句话,你可能有些看不懂,没关系,我会在下一讲详细的解释Lua++的一些API的用法。现在大概和你说一下,这句话的意思就是,把一个C++对象传输给Lua函数。
再看看,下面一个。

  1. bool CLuaFn:: PopLuaData(lua_State* pState, _ParamData* pParam, int nIndex)  
  2.   
  3. {  
  4.   
  5.         if(pParam == NULL)  
  6.   
  7.         {  
  8.   
  9.                 return false;  
  10.   
  11.         }  
  12.   
  13.   
  14.   
  15.         if(pParam->CompareType("string"))  
  16.   
  17.         {  
  18.   
  19.                 if (lua_isstring(m_pState, nIndex) == 1)  
  20.   
  21.                 {  
  22.   
  23.                         const char* pData = (const char*)lua_tostring(m_pState, nIndex);  
  24.   
  25.                         pParam->SetData((void* )pData, (int)strlen(pData));  
  26.   
  27.                 }  
  28.   
  29.                 return true;  
  30.   
  31.         }  
  32.   
  33.   
  34.   
  35.         if(pParam->CompareType("int"))  
  36.   
  37.         {  
  38.   
  39.                 if (lua_isnumber(m_pState, nIndex) == 1)  
  40.   
  41.                 {  
  42.   
  43.                         int nData = (int)lua_tonumber(m_pState, nIndex);  
  44.   
  45.                         pParam->SetData(&nData, sizeof(int));  
  46.   
  47.                 }  
  48.   
  49.                 return true;  
  50.   
  51.         }  
  52.   
  53.         else  
  54.   
  55.         {  
  56.   
  57.                 pParam->SetData(tolua_tousertype(m_pState, nIndex, NULL), -1);  
  58.   
  59.                 return true;  
  60.   
  61.         }  
  62.   
  63. }  

弹出一个参数并赋值。pParam->SetData(tolua_tousertype(m_pState, nIndex, NULL), -1);这句话同样,我在下一讲中详细介绍。
呵呵,好了,我们又进了一步,我们可以用这个函数绑定任意一个Lua函数格式。而代码不用多写,懒蛋的目的达到了。
呵呵,这一讲主要是介绍了一些基本知识,或许有点多余,但是我觉得是必要的,在下一讲中,我讲开始详细介绍如何绑定一个C++对象给Lua,并让Lua对其修改。然后返回结果。休息一下,休息一下先。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值