Lua脚本和C++交互(二)
上一节讲了一些基本的Lua应用,下面,我要强调一下,Lua的栈的一些概念,因为这个确实很重要,你会经常用到。熟练使用Lua,最重要的就是要时刻知道什么时候栈里面的数据是什么顺序,都是什么。如果你能熟练知道这些,实际你已经是Lua运用的高手了。当你初始化一个栈的时候,它的栈底是1,而栈顶相对位置是-1,说形象一些,你可以把栈想象成一个环,有一个指针标记当前位置,如果-1,就是当前栈顶,如果是-2就是当前栈顶前面一个参数的位置。以此类推。当然,你也可以正序去取,这里要注意,对于Lua的很多API,下标是从1开始的。这个和C++有些不同。而且,在栈的下标中,正数表示绝对栈底的下标,负数表示相对栈顶的相对地址,这个一定要有清晰的概念,否则很容易看晕了。(栈中数据的存储方式如下图)

注:其实都是从栈底到栈顶,索引数值递增。
下面看一些具体的例子:
lua_pushnumber(m_pState, );
lua_pushnumber(m_pState, ); // lua_gettop()这个API是告诉你目前栈里元素的个数
int nIn = lua_gettop(m_pState) int nData1 = lua_tonumber(m_pState, ); //读取栈底第一个绝对坐标中的元素
int nData2 = lua_tonumber(m_pState, ); //读取栈底第二个绝对坐标中的元素
int nData3 = lua_tonumber(m_pState, -); //读取栈顶第一个相对坐标中的元素
int nData4 = lua_tonumber(m_pState, -); //读取栈顶第二个相对坐标中的元素 printf("[Test]nData1 = %d, nData2 = %d./n, nData3 = %d, nData4 = %d./n");
[Test]nData1 = , nData2 = , nData3 = , nData4 =
上述代码的栈中存储如下图:

回到我上一节的那个代码:
bool CLuaFn::CallFileFn(const char* pFunctionName, int nParam1, int nParam2)
{
int nRet = ;
if (NULL == m_pState)
{
printf("[CLuaFn::CallFileFn]m_pState is NULL./n");
return false;
}
//验证你的Lua函数是否在你当前加载的Lua文件中,并把指针指向这个函数位置
lua_getglobal(m_pState, pFunctionName); // 压栈(顺序:从左到右的参数):第一个参数
lua_pushnumber(m_pState, nParam1);
// 压栈:第二个参数
lua_pushnumber(m_pState, nParam2); int nIn = lua_gettop(m_pState); //在这里加一行
//执行这个函数,2是输入参数的个数,1是返回值的个数
nRet = lua_pcall(m_pState, , , );
if (nRet != )
{
printf("[CLuaFn::CallFileFn]call function(%s) error(%d)./n", pFunctionName, nRet);
return false;
}
if (lua_isnumber(m_pState, -) == )
{
int nSum = lua_tonumber(m_pState, -);
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不会清除你以前栈内的数据,每调用一次都会给你生成一个新的栈元素插入其中。
#include "CLuaFn.h"
int main(int argc, char* argv[])
{
CLuaFn LuaFn;
LuaFn.Init();
LuaFn.LoadLuaFile("Sample.lua");
for (int i = ; i < ; i ++)
{
LuaFn.CallFileFn("func_Add", , );
}
getchar(); return ;
} bool CLuaFn::CallFileFn(const char* pFunctionName, int nParam1, int nParam2)
{
int nRet = ;
if (NULL == m_pState)
{
printf("[CLuaFn::CallFileFn]m_pState is NULL./n");
return false;
}
//验证你的Lua函数是否在你当前加载的Lua文件中,并把指针指向这个函数位置
lua_getglobal(m_pState, pFunctionName); // 压栈(顺序:从左到右的参数):第一个参数
lua_pushnumber(m_pState, nParam1);
// 压栈:第二个参数
lua_pushnumber(m_pState, nParam2); int nIn = lua_gettop(m_pState); //在这里加一行
//执行这个函数,2是输入参数的个数,1是返回值的个数
nRet = lua_pcall(m_pState, , , );
if (nRet != )
{
printf("[CLuaFn::CallFileFn]call function(%s) error(%d)./n", pFunctionName, nRet);
return false;
}
if (lua_isnumber(m_pState, -) == )
{
int nSum = lua_tonumber(m_pState, -);
printf("[CLuaFn::CallFileFn]Sum = %d./n", nSum);
}
int nOut = lua_gettop(m_pState); //在这里加一行
//lua_settop(m_pState, -2); //清除不用的栈
printf("[CLuaFn::CallFileFn]nOut = %d.\n", nOut);
return true;
}

那么怎么解决呢?呵呵,其实,如果不考虑多线程的话,在你的函数最后退出前加一句话,就可以轻松解决这个问题。(Lua栈操作是非线程安全的!)lua_settop(m_pState, -2);这句话的意思是什么?lua_settop()是设置栈顶的位置,我这么写,意思就是,栈顶指针目前在当前位置的-2的元素上。这样,我就实现了对栈的清除。仔细想一下,是不是这个道理呢?(运行结果如下图所示)

最后说一句,lua_tonumber()或lua_tostring()还有以后我们要用到的lua_touserdata()一定要将数据完全取出后保存到你的别的变量中去,否则会因为清栈操作,导致你的程序异常,切记!
Lua脚本和C++交互(二)的更多相关文章
- Lua脚本和C++交互(一)
现在,越来越多的C++服务器和客户端融入了脚本的支持,尤其在网游领域,脚本语言已经渗透到了方方面面,比如你可以在你的客户端增加一个脚本,这个脚本将会帮你在界面上显示新的数据,亦或帮你完成某些任务,亦或 ...
- (使用lua++)Lua脚本和C++交互(四)
上一篇中,你已经可以在Lua里面用C++的函数了,那么咱们再增加一点难度,比如,我有一个CTest对象,要作为一个参数,传输给func_Add()执行,怎么办?很简单,如果你对上面的代码仔细阅读,你会 ...
- (使用lua++)Lua脚本和C++交互(三)
前两篇文章中介绍了C++调用lua.lua栈操作的一些相关知识. 下面说一下Lua的工具.我们下一步要用到其中的一个帮助我们的开发,其实,Lua里面有很多简化开发的工具,你可以去www.sourcef ...
- Win32下 Qt与Lua交互使用(二):在Lua脚本中使用Qt类
话接上篇.成功配置好Qt+Lua+toLua后,我们可以实现在Lua脚本中使用各个Qt的类.直接看代码吧. #include "include/lua.hpp" #include ...
- 【COCOS2DX-LUA 脚本开发之十二】Hybrid模式-利用AssetsManager实现在线更新脚本文件lua、js、图片等资源(免去平台审核周期)
本站文章均为李华明Himi原创,转载务必在明显处注明:(作者新浪微博:@李华明Himi) 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/iphone-c ...
- Win32下 Qt与Lua交互使用(四):在Lua脚本中自由执行Qt类中的函数
话接上篇.通过前几篇博客,我们实现在Lua脚本中执行Qt类中函数的方法,以及在Lua脚本中连接Qt对象的信号与槽. 但是,我们也能发现,如果希望在Lua脚本中执行Qt类的函数,就必须绑定一个真正实现功 ...
- Win32下 Qt与Lua交互使用(三):在Lua脚本中connect Qt 对象
话接上文.笔者为了方便使用Lua,自己编写了一个Lua的类.主要代码如下: QLua.h #ifndef QLUA_H #define QLUA_H // own #include "inc ...
- redis(6)lua脚本
一.lua脚本 lua是一种轻量小巧的脚本语言,用标准的C语言编写并以源代码形式开放,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能. lua的详细内容你可以参考lua官方网站 ...
- Redis进阶应用:Redis+Lua脚本实现复合操作
一.引言 Redis是高性能的key-value数据库,在很大程度克服了memcached这类key/value存储的不足,在部分场景下,是对关系数据库的良好补充.得益于超高性能和丰富的数据结构,Re ...
随机推荐
- POJ 3253-Fence Repair(堆)
Fence Repair Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 27055 Accepted: 8800 Des ...
- java并发编程()阻塞方法与中断方法
看完这篇,我感觉我对java多线程又懵逼了. 线程可能会阻塞或暂停执行,原因有多种: 等待I/O操作结束 等待获得一个锁 等待从Thread.sleep方法中醒来 等待另一个线程计算的结果 当线程阻塞 ...
- Win10技巧:如何确定电脑是否适用Hyper-V虚拟机?
既然微软想要为Hyper-V的普及铺路,那么各种套路……配套措施当然也会一并跟上.比如想要看出电脑是否符合Hyper-V配置要求,有至少两种方式可以参考. 方法一:系统信息 这方法很简单,在Corta ...
- Android中使用SoundPool来播放音频
今天找素材重做FlappyBird时学习了一下怎样为应用设置背景音频,发现通过封装SoundPool类就能够非常好的做到这一点. SoundPool类比較适合播放一些类似游戏音效这样的比較短促并且较小 ...
- s:debug标签的错误ConcurrentModificationException
搭建SSH的时候页面上加入<s:debug>标签后台出现 严重: Servlet.service() for servlet jsp threw exception java.util.C ...
- 几个Tab,滑动门,选项卡,图片切换
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/stri ...
- OSX下git diff/merge 可视化工具 P4Merge 环境配置步骤
先下载 step1:首先,把要运行的命令放入外部包装脚本中,创建一个merge包装脚本,名字叫做extMerge,让它带参数调用P4Merge. $ cat >> /usr/local/b ...
- JQuery 获得元素的方法
获取一个元素的值可以有很多方式:<input type="text" id="txt" class="ipt" name=" ...
- Mysql经常使用基本命令汇总及默认账户权限与改动
一直仅仅是在浅显利用数据库存储数据.也被windows惯坏了.非常多命令使用的时候记不起来.so,换LINUX系统!不再使用GUI管理数据库!也想深入学习下Mysql.从权限管理開始.也就诞生了这篇学 ...
- 送给半路出家的Pythoner
伯乐在线Python专区: http://python.jobbole.com/category/python/ 我希望初学Python时就能知道的一些用法: http://python.jobbol ...