• lua_call/lua_pcall/xpcall


    vs2013+lua5.3.3

    1.涉及函数

    主要C函数:lua_call和lua_pcall

    主要lua函数xpcall

     

    2.正常使用lua_call

    ①hello.lua文件内容

    function ccall_test_func(val)
        print(string.format("ccall_test_func val = %d",val))
    end

     

    ②C++文件内容(主要内容在test_func函数中的lua_call函数,表示调用lua中的ccall_test_func函数,并将值1传给val)

    //这个头文件包含了所需的其它头文件
    #include "lua.hpp"
    
    //测试函数
    void test_func(lua_State* L){
        lua_getglobal(L, "ccall_test_func");
        lua_pushinteger(L, 1);
        lua_call(L, 1, 0);//调用
    }
    
    static const luaL_Reg lua_reg_libs[] = {
        { "base", luaopen_base },
        { "coroutine", luaopen_coroutine },
        { "table", luaopen_table },
        { "io", luaopen_io },
        { "os", luaopen_os },
        { "string", luaopen_string },
        { "utf8", luaopen_utf8 },
        { "math", luaopen_math },
        { "debug", luaopen_debug },
        { "package", luaopen_package },
        { NULL, NULL }
    };
    
    int main(int argc, char* argv[])
    {
        if (lua_State* L = luaL_newstate()){
    
            //注册让lua使用的库
            const luaL_Reg* lua_reg = lua_reg_libs;
            for (; lua_reg->func; ++lua_reg){
                luaL_requiref(L, lua_reg->name, lua_reg->func, 1);
                lua_pop(L, 1);
            }
            //加载脚本,如果出错,则打印错误
            if (luaL_dofile(L, "hello.lua")){
                std::cout << lua_tostring(L, -1) << std::endl;
            }
    
            test_func(L); //调用测试函数
    
            lua_close(L);
        }
        else{
            std::cout << "luaL_newstate error !" << std::endl;
        }
    
        system("pause");
    
        return 0;
    }

     

    ③运行后正常输出结果

     

    3.使用lua_call产生的问题

    ①将lua中文件的ccall_test_func函数修改为抛出一个error,如下: 

    function ccall_test_func(val)
        print(string.format("ccall_test_func val = %d",val))
    
        error("throw error") --新增加一行模拟抛出错误
    end

     

    ②运行后,直接抛出错误了

     

    4.方法一:使用lua的保护模式函数xpcall处理错误

    ①将hello.lua文件中的ccall_test_func函数修改为

    function ccall_test_func(val)
        --将函数调用修改为局部函数
        local function lua_test_func(val)
            print(string.format("ccall_test_func val = %d",val))
    
            error("throw error") --新增加一行模拟抛出错误
        end
    
        --调用保护函数xpcall
        local status, err_msg = xpcall( 
        function(v) lua_test_func(v) end,  --调用局部函数,v的值由下面的val提供
        function(msg) local ret_msg = debug.traceback(msg, 3) ret_msg = string.format("xpcall val = %d 
    %s", val, ret_msg) return ret_msg end,  --结合debug.traceback得到系统内部提供的堆栈信息msg,并将ret_msg作为xpcall的返回值
        val) --传给v
    
        if not status then
            print(err_msg)
        end
    end

     

    ②简化一下

    ③正常运行效果

     

    5.方法二:使用C++的保护模式函数lua_pcall处理错误

    ①在hello.lua中增加一个由C++调用的错误处理函数ccall_err_handler

    function ccall_test_func(val)
        print(string.format("ccall_test_func val = %d",val))
    
        error("throw error") --新增加一行模拟抛出错误
    end
    
    --增加的C++调用的错误处理函数
    function ccall_err_handler(msg)
        print(debug.traceback(msg,3))
    end

     

    ②修改C++中的test_func函数,使用保护模式函数lua_pcall

    //测试函数
    void test_func(lua_State* L){
        lua_getglobal(L, "ccall_err_handler");
        lua_getglobal(L, "ccall_test_func");
        lua_pushinteger(L, 1);
        lua_pcall(L, 1, 0, -3);//调用ccall_test_func,当出现错误时,会将error msg放在栈顶-1位置让放置在-3位置的错误处理函数ccall_err_handler使用
    }

     

    ②简化一下

     

     function ccall_test_func(val)
        --将函数调用修改为局部函数
        local function lua_test_func(val)
            print(string.format("ccall_test_func val = %d",val))
    
            error("throw error") --新增加一行模拟抛出错误
        end
    
        --错误处理函数
        local function error_handler(msg)
            local ret_msg = debug.traceback(msg, 3) 
            ret_msg = string.format("xpcall val = %d 
    %s", val, ret_msg) 
            return ret_msg 
        end
    
        local status, err_msg = xpcall(
        lua_test_func, --参数只有后面值val的话,这里可以直接使用函数名
        error_handler,  --参数只有msg的话(如果有其它传入参数,则需要使用上面的方式),这里可以直接使用函数名
        val) --传给val
    
        if not satus then
            print(err_msg)
        end
     end

     

     

     

     

    ③正常运行效果

     

    6.使用lua_pcall,增加输出用户信息

    ①从上面能看到,lua_pcall也能处理异常的情况,但是在异常信息里只包含了堆栈信息,没有对用户数据的描述 

     

    ②修改hello.lua的错误处理函数,增加参数接收val的值

    -- 增加的C++调用的错误处理函数
    function ccall_err_handler(val, msg)
        local err_msg = string.format("ccall_err_handler val = %d 
    %s", val, msg) -- 这里使用debug.traceback不起作用,因为栈已经展开,所以收集不到栈跟踪信息了
        print(err_msg)
    end

     

    ③修改一下C++中的test_func函数,如下

    //测试函数
    void test_func(lua_State* L){
        lua_getglobal(L, "ccall_test_func");
        lua_pushinteger(L, 1);
        if (lua_pcall(L, 1, 0, 0)){ //如果此时出现错误,则当前-1位置为error msg
            lua_getglobal(L, "ccall_err_handler"); //error msg变为-2
            lua_pushinteger(L, 1); //增加用户数据信息 //error msg变为-2
            lua_pushvalue(L, -3); //将-3位置的error msg 放置为-1位置
            lua_pcall(L, 2, 0, 0);
        }
    }

     

    ④正常运行效果

    上面可以看到数据信息以及错误信息了,但是堆栈跟踪信息收集不到了。

     

    综上所述:一般情况下,建议使用方法一,简单方便而且信息全。

     

    以下两点来自于 《Lua 5.3  参考手册》中文版 译者 云风 制作 Kavcc

    7.lua_pcall函数介绍

    int lua_pcall (lua_State *L, int nargs, int nresults, int msgh);

    以保护模式调用一个函数。

    nargs 和 nresults 的含义与 lua_call 中的相同。 如果在调用过程中没有发生错误, lua_pcall 的行为和 lua_call 完全一致。 但是,如果有错误发生的话, lua_pcall 会捕获它, 然后把唯一的值(错误消息)压栈,然后返回错误码。 同 lua_call 一样, lua_pcall 总是把函数本身和它的参数从栈上移除。

    如果 msgh 是 0 , 返回在栈顶的错误消息就和原始错误消息完全一致。 否则, msgh 就被当成是 错误处理函数 在栈上的索引位置。 (在当前的实现里,这个索引不能是伪索引。) 在发生运行时错误时, 这个函数会被调用而参数就是错误消息。 错误处理函数的返回值将被 lua_pcall 作为错误消息返回在堆栈上。

    典型的用法中,错误处理函数被用来给错误消息加上更多的调试信息, 比如栈跟踪信息。 这些信息在 lua_pcall 返回后, 由于栈已经展开,所以收集不到了。

    lua_pcall 函数会返回下列常数 (定义在 lua.h 内)中的一个:

    LUA_OK (0): 成功。
    LUA_ERRRUN: 运行时错误。
    LUA_ERRMEM: 内存分配错误。对于这种错,Lua 不会调用错误处理函数。
    LUA_ERRERR: 在运行错误处理函数时发生的错误。
    LUA_ERRGCMM: 在运行 __gc 元方法时发生的错误。 (这个错误和被调用的函数无关。)

     

    8.关于lua的错误处理

    由于 Lua 是一门嵌入式扩展语言,其所有行为均源于宿主程序中 C 代码对某个 Lua 库函数的调用。 (单独使用 Lua 时,lua 程序就是宿主程序。) 所以,在编译或运行 Lua 代码块的过程中,无论何时发生错误, 控制权都返回给宿主,由宿主负责采取恰当的措施(比如打印错误消息)。

    可以在 Lua 代码中调用 error 函数来显式地抛出一个错误。 如果你需要在 Lua 中捕获这些错误, 可以使用 pcall 或 xpcall 在 保护模式 下调用一个函数。

    无论何时出现错误,都会抛出一个携带错误信息的 错误对象 (错误消息)。 Lua 本身只会为错误生成字符串类型的错误对象, 但你的程序可以为错误生成任何类型的错误对象, 这就看你的 Lua 程序或宿主程序如何处理这些错误对象。

    使用 xpcall 或 lua_pcall 时, 你应该提供一个 消息处理函数 用于错误抛出时调用。 该函数需接收原始的错误消息,并返回一个新的错误消息。 它在错误发生后栈尚未展开时调用, 因此可以利用栈来收集更多的信息, 比如通过探知栈来创建一组栈回溯信息。 同时,该处理函数也处于保护模式下,所以该函数内发生的错误会再次触发它(递归)。 如果递归太深,Lua 会终止调用并返回一个合适的消息。

     

     

  • 相关阅读:
    SpringBoot部署jar与war
    Calendar代替Date常用方法小记
    Zookeeper注册中心底层实现小记
    1480. Running Sum of 1d Array
    700. Search in a Binary Search Tree
    1410. HTML Entity Parser
    1409. Queries on a Permutation With Key
    1408. String Matching in an Array
    1404. Number of Steps to Reduce a Number in Binary Representation to One
    1405. Longest Happy String
  • 原文地址:https://www.cnblogs.com/chevin/p/6040509.html
Copyright © 2020-2023  润新知