更多《C++十万个为什么》 文章

http://chenlq.net/dev/cpp-why/51-half-performance-half-of-the-flexible-cc-and-lua-hybrid-programming-how-to-call-c-functions-in-the-lua-script.html
Q:

在前一篇文章[50]一半是性能,一半是灵活——C++和Lua混合编程(如何在C++代码中调用Lua脚本的函数?)中,我们介绍了如何在C++程序中调用Lua脚本的函数来提高程序的灵活性,那么,反过来,我们是不是也可以在Lua脚本程序化中调用C++的函数,来提高程序的性能呢?如果可以,如何进行?
A:

回答当然是肯定的。我们这个系列的题目就是“一半是性能,一半是灵活”,也就是当C++需要灵活的时候,我们就可以在C++中调用Lua的函数来提高灵活性,反过来,当Lua需要性能的时候,我们自然也可以在Lua中调用C++函数来提高其性能。事物总是这样相辅相成,互相帮助。
我们还是来看一个实际的例子。在[50]一半是性能,一半是灵活——C++和Lua混合编程(如何在C++代码中调用Lua脚本的函数?)中,我们在C++程序中调用了Lua函数来定义游戏逻辑(判断某个点是否在矩形场景内),以此来提高程序的灵活性。现在,需求发生了变化(这个世界上,唯一不变的就是需求总是在变化),我们不再是判断某个点(x,y)是否在某个矩形场景(0,0,100,100)内,而是判断点是否在某个圆形场景(x=50,y=50,r=50)内。好在我们已经在C++程序中调用了Lua函数inscene()来进行逻辑判断,我们只需要简单地修改这个函数,就可以实现新的游戏逻辑:
 

--判断点(x,y)是否在圆形场景内
function inscene(x,y)
    --用lua的数学函数,计算点(x,y)到场景中心(50,50)的距离
    local len1 = math.pow(math.abs(x-50),2)
    local len2 = math.pow(math.abs(y-50),2)
    local dis = math.sqrt(len1+len2)
    --判断距离是否小于半径
    return  dis < 50
end

经过这样的简单修改,我们的C++程序就可以拥有新的游戏逻辑,从而轻松地满足了新的需求,这就是Lua脚本程序给C++程序带来的灵活性。
然而,问题又来了(一个旧的矛盾解决了,预示着一个新的矛盾的到来)。为了判断点是否在圆形场景之内,我们使用了Lua的数学函数计算了点到圆心的距离,可是,数学计算(或者说大量高负荷的计算 )是Lua这种脚本语言的弱项,而这恰恰是C/C++这种编译型语言的强项。一个很自然的想法就是在Lua脚本程序中调用C++函数,将大量计算的工作交给C++程序去完成,以此来提高Lua脚本程序的性能。
废话了这么多,才引入正题。要想在Lua脚本程序中调用C++函数,第一步自然是编写完成计算工作的C++函数。在这个例子中,最大的计算量集中在计算点到圆心的距离,所以我们编写一个C++函数来完成这个计算工作:
 

// 计算点到圆心的距离
static int distance(lua_State* L)
{
    int pos[4] = {0};
    // 用lua_tonumber()获取
    // 从Lua传递过来的点(pos[0],pos[1])和
    // 圆心(pos[2],pos[3])的坐标
    for(int i = 0; i < 4; ++i)
    {
        // 获取从Lua传递过来的参数数据
        // 这里需要注意的是,这些数据是以1开始的
        // 所以我们这里用i+1作为序号
        // 如果序号是0,得到的是参数数据的个数
        pos[i] = luaL_checkint(L, i+1);
    }
    // 用C++的数学函数计算点到圆心的距离
    int a = pow(abs(pos[0]-pos[2]),2);
    int b = pow(abs(pos[1]-pos[3]),2);
    double dis = sqrt(a + b);
    // 将计算得到的距离压入栈顶作为返回结果
    lua_pushnumber(L,dis);
    return 1; // 返回返回值的个数
}

这里我们可以看到,整个过程其实很简单,无非就是将参数类型设置为lua_State*,然后用luaL_checkint()获得从Lua传递过来的点和圆心的坐标,接着就利用这些坐标,通过C++的数学函数计算两点之间的距离(这个自然是比Lua的计算要更加高效),最后通过lua_pushnumber()将结果数据传递给Lua程序,并最终返回结果数据的个数。
完成C++函数的实现后,为了让Lua脚本程序能够调用这个函数,我们还必须用lua_regiser()将这个函数注册,让Lua脚本可以找到这个函数:
 

// C++程序执行Lua脚本
int main()
{
    // Lua解释器指针
    lua_State* L = nullptr;
    // 创建Lua解释器
    L = luaL_newstate();
    if(nullptr != L)
    {
        luaL_openlibs(L);// 打开Lua库
        lua_register(L, "distance", distance);// 注册lua基本库,绑定之
        // 执行Lua脚本snake.lua
        luaL_dofile(L, "snake.lua");
        //...
    }
    return 0;
}

最后,自然就是在Lua脚本程序中调用这个函数,用C++来高效地计算距离:
 

--通过调用C++的distance函数,
--判断点(x,y)是否在场景内
function inscene(x,y)
    --调用C++的distance函数,
    --计算点(x,y)到场景中心(50,50)的距离
    local dis = distance(x,y,50,50);
    return dis < 50
end

通过这样的混和编程,我们在C++程序中调用了Lua函数,增加了程序的灵活性,在Lua程序中调用了C++函数,提高了程序的性能。两相结合,各取所长,这就是混合编程的意义所在。
最后,补充一下这个例子完整的C++代码:
 

// 引入Lua需要的头文件
// 如果是C语言代码,extern "C"可以省略
extern "C"
{
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
};
#include <iostream>
#include <cmath>
using namespace std;
// 计算点到圆心的距离
static int distance(lua_State* L)
{
    int pos[4] = {0};
    // 用lua_tonumber()获取
    // 从Lua传递过来的点(pos[0],pos[1])和
    // 圆心(pos[2],pos[3])的坐标
    for(int i = 0; i < 4; ++i)
    {
        // 获取从Lua传递过来的参数数据
        // 这里需要注意的是,这些数据是以1开始的
        // 所以我们这里用i+1作为序号
        // 如果序号是0,得到的是参数数据的个数
        pos[i] = luaL_checkint(L, i+1);
    }
    // 用C++的数学函数计算点到圆心的距离
    int a = pow(abs(pos[0]-pos[2]),2);
    int b = pow(abs(pos[1]-pos[3]),2);
    double dis = sqrt(a + b);
    // 将计算得到的距离压入栈顶作为返回结果
    lua_pushnumber(L,dis);
    return 1; // 返回返回值的个数
}
// 判断x,y是否在场均内
bool inscene(lua_State* L,int x,int y)
{
    bool isin = false;  // 初始值,未在场景内
    if(nullptr != L)
    {
        // 根据函数名获得Lua中的函数
        lua_getglobal(L, "inscene");
        lua_pushnumber(L, x); // 参数一入栈
        lua_pushnumber(L, y); // 参数二入栈
        // 调用Lua中的inscene函数,
        // 这里的2,1表示两个输入参数,一个返回值
        lua_call(L, 2, 1);
        // 获得bool类型的返回值
        isin = lua_toboolean(L, -1);
        lua_pop(L, 1);// 将返回值出栈,恢复栈中的元素
    }
    return isin;
}
// C++程序执行Lua脚本
int main()
{
    // Lua解释器指针
    lua_State* L = nullptr;
    // 创建Lua解释器
    L = luaL_newstate();
    if(nullptr != L)
    {
        luaL_openlibs(L);// 打开Lua库
        lua_register(L, "distance", distance);// 注册lua基本库,绑定之
        // 执行Lua脚本snake.lua
        luaL_dofile(L, "snake.lua");
        int x = 120;
        int y = 20;
        // 调用Lua脚本中的函数
        bool isin = inscene(L, x, y);
        // 输出结果
        if(isin)
            std::cout<<x<<","<<y<<" is in the scene."<<std::endl;
        lua_close(L);// 关闭Lua
    }
    return 0;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值