【Programming In Lua (2E) 笔记】4:用lua扩展C++——C++调用lua函数

原创 2016年05月31日 23:26:58

前言

本文介绍如何在C++中调用lua的function,lua很多情况下是被用做一种扩展语言,它的function更是增加了这门扩展语言的灵活性,赋予了lua生命力,使它变化莫测。在lua的function中还可以回调宿主语言的函数。这篇文章展示如何从C++端调用lua函数,相反的过程在后面的文章再做介绍。

本文使用的Lua版本还是5.1。

简单的调用示例

还是使用上一篇文章中使用的环境,调用config.lua里面的f函数:

res/config.lua:

function f(var)
    return var * var + 2 * var + 100
end

main.cpp:

int main() {

    lua_State* lua = luaL_newstate();

    // 加载config.lua
    luaL_loadfile(lua, "res/config.lua");
    lua_pcall(lua, 0, 0, 0);

    lua_getglobal(lua, "f");        // 获得全局函数f

    if (!lua_isfunction(lua, -1)) {
        error(lua, "f is not a function");
    }

    lua_pushnumber(lua, 10);        // 压入参数 10

    int luaError = lua_pcall(lua, 1, 1, 0);     // 调用函数f,传入1个参数,返回1个参数,不使用错误处理函数
    if (luaError) {
        error(lua, "fail to call f: %s", lua_tostring(lua, -1));
    }

    double result = lua_tonumber(lua, -1);      // 得到返回值

    psln(result); // result = 10*10 + 2 * 10 + 100 = 220

    lua_close(lua);

    return 0;
}

lua_pcall

其实这个例子里面出现的几个API在前两篇文章中都已经见过,只不过没有用来调用function。这里面调用function的API就是lua_pcall, 它的原型是:

LUA_API int (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc);

第一个参数是lua状态,第二个参数是输入参数个数,第三个参数是返回值个数,最后一个参数是错误处理函数.

注意:无论在执行正常还是出错,之前在栈中的函数和输入参数都会被弹出;如果调用失败,会压入一条错误信息;如果调用成功, lua_pcall先弹出函数和输入参数,然后压入返回值,如果返回值个数多于指定的返回值个数nresults, 那么多余的会被丢弃;如果小于nresults,则会压入nil来凑数。

关于错误处理函数

在上面的例子中,没有使用错误处理函数,lua_pcall的第四个参数传入0即表示不设置错误处理函数。注意到这个参数是一个整数,它的含义是错误处理函数在虚拟栈中的索引。因此,如果我想指定错误处理函数,那么要在调用函数之前,把错误处理函数先压入栈, 而且要先于函数和其参数。当lua_pcall调用失败,在压入错误信息之前,会先调用这个错误处理函数。

lua_pcall的返回值

  • 0: 调用成功;

  • 非0: 调用错误;

有没有与lua_pcall类似的调用函数?

封装调用函数

在PIL第25.4节,介绍了一个通用的调用函数,其功能如下:

对于如下的lua脚本,

test.lua:

function add(a, b)
    return a + b
end

我要在C++中调用它的add方法:

double x, y, z;
x = 10, y = 20, z = 0;

call_va("add", "dd>d", x, y, &z);

assert(z == 30);

这里的call_va就是所说的通用调用函数,

  • add: 表示要调用的lua函数名字

  • dd>d: d 表示double, >之前的两个d,表示有两个输入参数分别是double, double; >后面的一个d表示返回一个double

  • x : 对应于dd>d中第1个d, 输入参数

  • y : 对应于dd>d中第2个d, 输入参数

  • z : 对应于dd>d中第3个d, 输出参数 —— 即返回值。

下面给出的call_va函数的实现与书中实现上略有区别,PIL是C语言风格的代码,这里是C++风格的代码:

void call_va(const std::string& script, const std::string& funcName, const std::string& signature, ...) {

    lua_State* lua = luaL_newstate();

    if (luaL_loadfile(lua, script.c_str()) || lua_pcall(lua, 0, 0, 0)) {
        error(lua, "fail to load script: %s, %s", script.c_str(), lua_tostring(lua, -1));
    }

    lua_getglobal(lua, funcName.c_str());
    if (!lua_isfunction(lua, -1)) {
        error(lua, "%s should be a function", funcName.c_str());
    }

    va_list varg;
    va_start(varg, signature);

    int argCount(0);
    std::string returnStr;
    auto iter = signature.begin();
    while (iter != signature.end()) {
        if (*iter == '>') {
            copy(++iter, signature.end(), std::back_inserter(returnStr));
            break;
        }
        else if (*iter == 'd') {
            lua_pushnumber(lua, va_arg(varg, double));
        }
        else if (*iter == 'i') {
            lua_pushinteger(lua, va_arg(varg, int));
        }
        else if (*iter == 's') {
            lua_pushstring(lua, va_arg(varg, char*));
        }
        else if (*iter == 'b') {
            lua_pushboolean(lua, va_arg(varg, bool));
        }
        else {
            error(lua, "unknown arg type: %c", *iter);
        }
        ++iter;
        ++argCount;
    }

    int numberOfReturn = returnStr.length();
    if (lua_pcall(lua, argCount, numberOfReturn, 0) != 0) {
        error(lua, "fail to call %s", funcName.c_str());
    }

    iter = returnStr.begin();
    int returnValueLeft = numberOfReturn;
    while (iter != returnStr.end()) {
        if (returnValueLeft < 1) {
            error(lua, "too much return value specified in signature");
        }

        if (*iter == 'd') {
            if (!lua_isnumber(lua, -returnValueLeft)) {
                error(lua, "%d-th return value should be double", numberOfReturn - returnValueLeft + 1);
            }
            *va_arg(varg, double*) = lua_tonumber(lua, -returnValueLeft);
        }     
        else if (*iter == 'i') {
            if (!lua_isnumber(lua, -returnValueLeft)) {
                error(lua, "%d-th return value should be int", numberOfReturn - returnValueLeft + 1);
            }
            *va_arg(varg, int*) = lua_tointeger(lua, -returnValueLeft);
        }
        else if (*iter == 'b') {
            if (!lua_isboolean(lua, -returnValueLeft)) {
                error(lua, "%d-th return value should be boolean", numberOfReturn - returnValueLeft + 1);
            }
            *va_arg(varg, bool*) = lua_toboolean(lua, -returnValueLeft);
        }
        else {
            error(lua, "unsupported return type: %c", *iter);
        }
        ++iter;
        --returnValueLeft;
    }
    va_end(varg);
    lua_close(lua);
    pv("function call %s(...) complete!\n", funcName.c_str());
}

测试用例:

lua脚本:res/extend_demo.lua

function add(x, y)
    return x + y
end

function concat(str, n)
    -- print(str .. tostring(n))
end

function fufudezheng(x, y)
    -- print(type(x))
    -- print(type(y))
    if not x and not y then
        return true
    elseif x and y then
        return true
    else
        return false
    end
end
void testLuaCaller() {

    std::string scriptName("res/extend_demo.lua");

    int x, y, z;
    x = 10; y = 20; z = 0;
    callLuaFunction(scriptName, "add", "ii>i", x, y, &z);
    psln(z);                    // z = 30

    int n(10);
    const char* prefix = "hello";
    callLuaFunction(scriptName, "concat", "si", prefix, n);

    double dx, dy, dz;
    dx = 1.2, dy = 1.3, dz = 0;
    callLuaFunction(scriptName, "add", "dd>d", dx, dy, &dz);
    psln(dz);                   // dz = 2.5

    bool bx, by, bz;
    bx = false, by = false, bz = true;
    callLuaFunction(scriptName, "fufudezheng", "bb>b", bx, by, &bz);
    psln(bz);                   // bz = 1   (true)

}

这个实现还要修改一下:

  • 兼容返回char*

  • 在C++中打开lua标准库,让res/extend_demo.lua中可以使用lua标准库里的函数

修改之后再回来修改 to be continue…


作者水平有限,对相关知识的理解和总结难免有错误,还望给予指正,非常感谢!

在这里也能看到这篇文章:github博客, CSDN博客, 欢迎访问

版权声明:本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名elloop(包含链接)

相关文章推荐

【Programming In Lua (2E) 笔记】1:从源码安装lua

本文以lua-5.1为例,说明如何从源代码生成二进制的lua和luac可执行文件。PC操作系统以Mac为例。

cocos2dx中注册导出c++函数供lua调用(使用tolua++)

弄了半天终于搞定了,网上有很多相似的帖子,但可能由于我基础不好,总是出错,最后搞定了。 参考http://www.cnblogs.com/mrblue/archive/2013/06/08/31269...

Cocos2d-x下Lua调用自定义C++类和函数的最佳实践

转载自:http://segmentfault.com/blog/hongliang/1190000000631630 关于cocos2d-x下Lua调用C++的文档看了不少,但没有一篇真正...

cocos2dx关于在c/c++中调用lua函数

关于在c/c++中调用lua函数

cocos2d-x 3.0 在C++中调用lua函数

代码用的是cocos2d-x 3.0 在lua中调用自定义类>中的代码.  在上篇的基础上进行扩充. 写lua函数 local function process_pa...

Cocos2d-x下Lua调用自定义C++类和函数的最佳实践

关于cocos2d-x下Lua调用C++的文档看了不少,但没有一篇真正把这事给讲明白了,我自己也是个初学者,摸索了半天,总结如下: cocos2d-x下Lua调用C++这事之所以看起来这么复杂、...

Cocos2d-x下Lua调用自定义C++类和函数的最佳实践

[转载出处] http://segmentfault.com/blog/hongliang/1190000000631630 关于cocos2d-x下Lua调用C++的文档看了不少,但没有一篇真...

Cocos2d-x下Lua调用自定义C++类和函数的最佳实践

http://segmentfault.com/a/1190000000631630 关于cocos2d-x下Lua调用C++的文档看了不少,但没有一篇真正把这事给讲明白了,我自己也是个初学...
  • ltxie
  • ltxie
  • 2016-01-15 17:17
  • 49

Cocos2d-x下Lua调用自定义C++类和函数的最佳实践

转载自http://segmentfault.com/a/1190000000631630 关于cocos2d-x下Lua调用C++的文档看了不少,但没有一篇真正把这事给讲明白了,我自己也是个初...

lua调用C/C++函数

内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)