我有一个可以从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则不会调用字符串析构函数。通过编译为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
需要添加到许多地方,因此非常难看且容易出错。它可以作为一个宏来完成,并放置每个可以抛出的命令,但这不会更好。
答案 0 :(得分:8)
答案 1 :(得分:4)
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);
}
// transform exception with description into lua_error
catch(exception &e){
luaL_error(L, e.what());
}
// rethrow lua error - C++ Lua throws lua_longjmp*
catch(lua_longjmp*){
throw;
}
// any other exception as lua_error with no description
catch(...){
luaL_error(L, "Unknown error");
}
return result;
}
template<lua_CFunction func>
int SafeFunction(lua_State *L){
return SafeFunctionCommon(L, func);
}
答案 2 :(得分:1)
我不会使用lua_error来表示在lua功能之外发生的错误。如果要添加可在lua范围内调用的其他lua函数,则将使用lua_error。如果在执行该函数时发生错误,则lua_error将是合适的。
这也是Stack unwinding in C++ when using Lua
的副本修改强>
如果您担心被调用的字符串析构函数,为什么不这样做:
try
{
string str("Hello");
lua_pushlstring(L, str.data(), str.length());
}
catch (exception& e)
{
luaL_error(L, e.what());
}
我意识到这是对你建议的一个微妙的改变但是有区别。如果抛出异常,try{}
内的堆栈中的任何内容都将被破坏。只需确保您想要破坏的任何内容都在该尝试中。
答案 3 :(得分:1)
Lua不会捕获C ++异常。如果你没有捕获它,它将被传递到调用堆栈,直到它被其他代码块捕获或导致程序崩溃(未处理的异常)。如果您向Lua调用的函数调用可以抛出异常的函数,则应该在该函数中处理它们。
答案 4 :(得分:0)
如果您将Lua编译为C ++,那么他们将使用C ++异常作为错误,而如果您编译为C则他们将使用longjmp / setjmp。这基本上意味着抛出这样的例外没有什么大不了的。