当从Lua调用函数时如何处理C++异常?
我有一个可以从 Lua 中调用的工作 C++ 函数。为了说明我的问题,这里有一个例子:
int PushHello(lua_State *L){
string str("Hello");
lua_pushlstring(L, str.data(), str.length());
return 1;
}
注意:我知道我不必在那里使用字符串变量,但它存在于演示问题。
我有两个问题:
当我从 Lua 中调用此函数时,字符串构造函数可能会抛出异常。那是个问题吗?Lua 会处理它并正确地解开 Lua 栈吗?我不这么认为。我如何解决这个问题?我需要在所有这样的代码周围添加
try/catch
并将异常转换为lua_error
吗?没有更好的解决方案吗?我可能已经通过将 Lua 编译为 C++ 来解决了另一个问题,就是当
lua_pushlstring()
调用lua_error()
时,如果使用了 longjmp,则不会调用字符串析构函数。通过将 Lua 编译为 C++ 并使用异常来代替 longjmp,是否已经解决了这个问题?
为了澄清,我可以看到问题 1 的可能解决方案是这样的:
int PushHello(lua_State *L){
string str;
try{
str.assign("Hello");
catch(exception &e){
luaL_error(L, e.what());
}
lua_pushlstring(L, str.data(), str.length());
return 1;
}
但这非常丑陋和容易出错,因为 try/catch
需要添加到许多地方。它可以作为宏完成,并放置在可能抛出异常的每个命令周围,但那也不会更好。
原文链接 https://stackoverflow.com/questions/4615890
如果你将 Lua 编译为 C++,则它们将使用 C++ 异常作为错误,而如果你将其编译为 C,它们将使用 longjmp/setjmp
。这基本上意味着抛出这样的异常没有什么大不了的。
Lua 不会捕获 C++ 异常。如果您没有捕获它,它就会沿着调用堆栈传递,直到被其他代码块捕获或导致程序崩溃(未处理的异常)。如果您向 Lua 暴露的函数调用可以抛出异常的函数,您应该在该函数中处理它们。
我找到了一个合理的解决方案。问题是它是否正确。不是导出(或通过lua_cpcall调用)原始函数 int PushHello(lua_State *L)
,而是导出/调用包装器 int SafeFunction<PushHello>(lua_State *L)
。包装器看起来像这样:
template<lua_CFunction func>
int SafeFunction(lua_State *L){
int result = 0;
try{
result = func(L);
}
// 将带描述的异常转换为lua_error
catch(exception &e){
luaL_error(L, e.what());
}
// 重新抛出lua错误 - C++ Lua抛出lua_longjmp*
catch(lua_longjmp*){
throw;
}
// 以无描述的lua_error形式捕获任何其他异常
catch(...){
luaL_error(L, "Unknown error");
}
return result;
}
你认为这个方案怎么样?你看到了什么问题吗?
Juraj Blaho的回答很棒。但是它有个缺点:对于每个使用 int SafeFunction<PushHello>(lua_State *L)
导出的函数,编译器都会生成模板中所有代码的副本,就像它是一个宏一样。当导出许多小型函数时,这将浪费空间。
你可以通过定义一个公共的 static
函数来避免这个问题,用该函数执行所有的工作,而 template
函数只是调用这个公共函数:
static int SafeFunctionCommon(lua_State *L, lua_CFunction func){
int result = 0;
try{
result = func(L);
}
// 将描述异常的信息转换为 lua_error
catch(exception &e){
luaL_error(L, e.what());
}
// 重新抛出 lua error - C++ Lua 抛出 lua_longjmp*
catch(lua_longjmp*){
throw;
}
// 其他任何异常都被当作没有描述的 lua_error
catch(...){
luaL_error(L, "Unknown error");
}
return result;
}
template<lua_CFunction func>
int SafeFunction(lua_State *L){
return SafeFunctionCommon(L, func);
}
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?
- addEventListener 返回 nil Lua
- Lua中获取用户配置主目录的跨平台方法
- 如何编写 Lua 模式将字符串(嵌套数组)转换为真正的数组?
我不会使用lua_error来表示在lua功能范围之外发生的错误。如果您正在添加可以在lua范围内调用的附加lua函数,那么当执行该函数时发生错误时,可以使用lua_error。
此外,这是在使用Lua时进行C ++堆栈展开的副本
编辑
如果您担心字符串析构函数被调用,那么为什么不这样做:
try { string str("Hello"); lua_pushlstring(L, str.data(), str.length()); } catch (exception& e) { luaL_error(L, e.what()); }
我意识到这与您建议的有微妙的差别,但是有所不同。如果抛出异常,则在
try {}
中的堆栈上的任何内容都将析构。只需确保您想要析构的任何内容都在该try之内。