C++ 与 Lua 交互异常处理

一、异常处理

Lua 使用了 C 语言的 setjmp 机制,setjmp 营造了一个类似异常处理的机制。因此大多数 API 函数都可以抛出异常(即调用函数 longjmp)而不是直接返回。

这里关注的视角是 C/C++ 提供一个运行环境,Lua 作为一个脚本在其中运行,所以最为重要的是脚本的异常不影响宿主的正常运行。 要做到这一环节最重要的是考虑如何处理好其中的异常流程。归纳起来有四种交互方式:

  • C++ 调用 C++ 代码,这只是纯粹的 C++ 编码,不在 C++ 和 Lua 交互的涉猎范围,需要的话可以查阅 C++ 异常捕获的相关文章。
  • C++ 调用 Lua 代码,这一过程发生在宿主 C++ 驱动 Lua 脚本代码,Lua 代码中可能会发生异常,这一异常需要让 C++ 能够感知到并处理,这一过程会用到 lua_pcall (下面会详细分享) 。
  • Lua 调用 Lua 代码,这一过程发生在脚本代码调用脚本代码,则使用 pcall 函数(详细使用看前面分享的 《Lua 编译执行和错误处理》 文章)。或是发生在脚本代码调用自己封装的 Lua 库代码(详细可以看前面分享的 《Lua 模块与包》)。这两种调用方式都有可能发生异常,两者都能在返回值接收到异常信息,所以这里更多是业务层面需要的如何处理,即按需求是中断流程还是继续流程。
  • Lua 调用 C++ 代码,这一过程发生在 Lua 调用宿主的能力,或是 Lua 调用 C 标准库的能力,这里的标准库可能会有异常的发生,C/C++ 代码异常则需要自行捕获(就和 “第一点” 一样),或是自身流程需要抛出异常,这过程需要让 Lua 脚本感知,则需要通过 lua_error (下面会详细分享)进行抛回给 Lua 脚本,而不是直接 throw 之类的抛出异常。

第一小点不在我们这系列文章的涉猎范围,也就不详细分享了。

第三小点在前面的文章已经详细分享,需要的话可以翻阅之前的文章,也就不赘述了。

我们接下来基于第二点和第四点进行详细的分享。

Lua 编译执行和错误处理 https://mp.weixin.qq.com/s?__biz=Mzg2MzQ0MDA1OA==&mid=2247484254&idx=1&sn=39dfd00b84d047a6fe3427855605a9eb&chksm=ce79de8bf90e579d4d7bf52f6d7c22e88128cd6ec83b8e66dd439ea7ffed13fede6228a6c23a&token=2038877152&lang=zh_CN#rd

Lua 模块与包 https://mp.weixin.qq.com/s?__biz=Mzg2MzQ0MDA1OA==&mid=2247484330&idx=1&sn=ae8078abc9d001931c9dd2a7b2b42474&chksm=ce79de7ff90e57691a41c0a1471d54f3be753a0eace39fbf8b663adf9373b6781eacde9f0907&token=2038877152&lang=zh_CN#rd

二、C++ 处理 Lua 异常

1、C++ 如何运行 Lua 代码

可以通过 lua_calllua_pcall 两个函数调用 Lua 代码。

int lua_call(lua_State *L, int nargs, int nresults);

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

两者均用于在 C/C++ 代码中调用 Lua 函数,不同点在于:

  • lua_call 会将代码中的异常直接抛出,导致程序中断。
  • lua_pcall 提供一个保护模式运行 Lua 代码,即使发生异常,也会被捕获,并可以通过第四个参数的错误处理函数处理错误,程序不会因此而中断。

参数:

  • L:Lua State 的指针。
  • nargs:传递给被调用函数的参数个数。
  • nresults:期望的返回值个数。
  • errfunc:错误处理函数的索引,用于处理发生的错误。如果为 0,则错误信息会被压入栈顶。

返回值:

  • 函数调用成功,返回 0,并将返回值压入栈中。
  • 如果函数调用发生错误,返回一个非零值,并将错误信息压入栈中。

2、lua_pcall 的错误处理函数

错误处理函数的格式为一个返回 int 类型,入参为 lua_State 类型的函数

int functionName(lua_State *L);

传递给 “错误处理函数” 的 lua_State 中的栈是一个新的栈,和调用 lua_pcall 的函数栈并非同一个。栈中有一个元素,即 Lua 代码中抛出的异常。

可以在 “错误处理函数” 内部通过这个元素得知 Lua 代码中的异常信息,然后可以对错误信息进行加工后再压入栈,最后返回给 C/C++ 宿主程序,C/C++ 宿主程序通过 lua_State 的栈中栈顶元素即为错误信息。

Lua 的异常信息可以是 Lua 自行抛出的,例如对一个 nil 变量调用了一个方法,或是程序员因为业务流程的需要使用 error 函数(具体使用可以查看前面分享的 《Lua 编译执行和错误处理》 文章)抛出异常。所以这一异常并不一定是字符串,可以是 table 类型等 Lua 的数据类型。

3、举个例子

举一个例子捋顺这一小节的使用流程,下面例子的流程为:

  1. 通过 C++ 调用 Lua 的脚本代码
  2. 在 Lua 的脚本代码中抛出异常
  3. 然后 C++ 的错误处理函数中增加 Lua 抛出异常的堆栈信息,压入栈中返回给调用点
  4. 最后调用点,即第一点的调用处获取 Lua 脚本的运行结果,如果失败则打印错误信息

先看 C++ 调用的代码,其中 safeCallLua 是对 lua_pcall 进行了封装,可以看最后面的代码段

lua_State *L = luaL_newstate();

// 加载函数库,否则加载的 Lua 无法使用内置库
luaL_openlibs(L);

auto luaFilePath = PROJECT_PATH + "/3、C++调用Lua的错误处理和内存分配/C++调用Lua异常处理/C运行的Lua文件.lua";

// 加载 Lua 脚本
auto loadLuaResult = luaL_loadfile(L, luaFilePath.c_str());
if (loadLuaResult) {
   
    printf("加载 Lua 文件失败. file: %s\n", lua_tostring(L, -1));
    return;
}

// 压入参数,传递给 Lua 脚本
lua_pushstring(L, 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值