zoukankan      html  css  js  c++  java
  • Lua脚本和C++交互(二)

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

    注:其实都是从栈底到栈顶,索引数值递增

    下面看一些具体的例子:

    lua_pushnumber(m_pState, 11);
    lua_pushnumber(m_pState, 12);
    
    // lua_gettop()这个API是告诉你目前栈里元素的个数
    int nIn = lua_gettop(m_pState)
    
    int nData1 = lua_tonumber(m_pState, 1);     //读取栈底第一个绝对坐标中的元素
    int nData2 = lua_tonumber(m_pState, 2);     //读取栈底第二个绝对坐标中的元素
    int nData3 = lua_tonumber(m_pState, -1);     //读取栈顶第一个相对坐标中的元素
    int nData4 = lua_tonumber(m_pState, -2);     //读取栈顶第二个相对坐标中的元素
    
    printf("[Test]nData1  = %d, nData2  = %d./n, nData3  = %d, nData4  = %d./n");
    [Test]nData1
    = 11, nData2 = 12, nData3 = 12, nData4 = 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函数是否在你当前加载的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, 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不会清除你以前栈内的数据,每调用一次都会给你生成一个新的栈元素插入其中。

    #include "CLuaFn.h"
    int main(int argc, char* argv[])
    {
    CLuaFn LuaFn;
    LuaFn.Init();
    LuaFn.LoadLuaFile("Sample.lua");
    for (int i = 1; i < 100; i ++)
    {
      LuaFn.CallFileFn("func_Add", 11, 12);
    }
    getchar();
    
    return 0;
    }
    
    
    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函数是否在你当前加载的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, 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);             //清除不用的栈
        printf("[CLuaFn::CallFileFn]nOut = %d.
    ", nOut);
        return true;
    }

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

    最后说一句,lua_tonumber()或lua_tostring()还有以后我们要用到的lua_touserdata()一定要将数据完全取出后保存到你的别的变量中去,否则会因为清栈操作,导致你的程序异常,切记!

  • 相关阅读:
    服务器电源管理(Power Management States)
    MSSQLSERVER数据库- 杂记
    MSSQLSERVER数据库- 判断全局临时表是否存在
    MSSQLSERVER数据库- 获取月份的第一天和最后一天
    JQuery- 解析JSON数据
    恼人的Visual Studio 2010崩溃重启问题
    MSSQLSERVER数据库- 游标
    Js/Jquery- Base64和UrlEncode编码解码
    MSSQLSERVER数据库- 解决不允许保存更改表结构
    Java- Jdbc学习
  • 原文地址:https://www.cnblogs.com/yyxt/p/4021916.html
Copyright © 2011-2022 走看看