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

时间:2011-01-06 14:22:40

标签: c++ exception lua

我有一个可以从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则不会调用字符串析构函数。通过编译为C ++并抛出异常而不是使用longjmp来解决问题吗?

  3. 为了澄清,我可以看到问题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需要添加到许多地方,因此非常难看且容易出错。它可以作为一个宏来完成,并放置每个可以抛出的命令,但这不会更好。

5 个答案:

答案 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。这基本上意味着抛出这样的例外没有什么大不了的。