当从Lua调用函数时如何处理C++异常?

我有一个可以从 Lua 中调用的工作 C++ 函数。为了说明我的问题,这里有一个例子:

int PushHello(lua_State *L){
    string str("Hello");
    lua_pushlstring(L, str.data(), str.length());
    return 1;
}

注意:我知道我不必在那里使用字符串变量,但它存在于演示问题。

我有两个问题:

  1. 当我从 Lua 中调用此函数时,字符串构造函数可能会抛出异常。那是个问题吗?Lua 会处理它并正确地解开 Lua 栈吗?我不这么认为。我如何解决这个问题?我需要在所有这样的代码周围添加 try/catch 并将异常转换为 lua_error 吗?没有更好的解决方案吗?

  2. 我可能已经通过将 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

点赞
stackoverflow用户553308
stackoverflow用户553308

我不会使用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之内。

2011-01-06 14:34:18
stackoverflow用户298661
stackoverflow用户298661

如果你将 Lua 编译为 C++,则它们将使用 C++ 异常作为错误,而如果你将其编译为 C,它们将使用 longjmp/setjmp。这基本上意味着抛出这样的异常没有什么大不了的。

2011-01-06 14:51:52
stackoverflow用户529761
stackoverflow用户529761

Lua 不会捕获 C++ 异常。如果您没有捕获它,它就会沿着调用堆栈传递,直到被其他代码块捕获或导致程序崩溃(未处理的异常)。如果您向 Lua 暴露的函数调用可以抛出异常的函数,您应该在该函数中处理它们。

2011-01-06 14:56:05
stackoverflow用户426288
stackoverflow用户426288

我找到了一个合理的解决方案。问题是它是否正确。不是导出(或通过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;
}

你认为这个方案怎么样?你看到了什么问题吗?

2011-01-06 18:01:11
stackoverflow用户1008957
stackoverflow用户1008957

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);
}
2012-11-24 21:16:49