为什么当 LuaJIT 抛出异常时 std::uncaught_exception 函数返回 false?

LuaJIT 手册 说明:

用 catch(...) 可以在 C++ 代码中捕获 Lua 异常。可以从 Lua 堆栈中获取相应的 Lua 异常信息。

这个功能可以正常运行,但是 std::uncaught_exception() 函数却不返回 true。

下面是一个最小示例,以说明此问题。checker 的析构函数应在堆栈展开期间执行,因此其中的 std::uncaught_exception() 应该返回 true,但它没有。

这是为什么?我是否误解了流程,或者 LuaJIT 进行了一些问题的异常抛出?

struct checker {
    ~checker() {
        if(std::uncaught_exception()) {
            // 此条件应评估为 true,但实际上不是
        }
    }
}

auto l = luaL_newstate();
try {
    {
        checker c;
        luaL_checknumber(l, -1); // 这行代码会引发 LuaJIT 异常
    }
}
catch(...) {
    // 这将被执行,按预期工作
    auto err = lua_tostring(state, -1); // 读取 LuaJIT 错误,此功能也正常
    // ...
}
点赞
用户734069
用户734069

std::uncaught_exception 是 C++ 异常处理机制的一个函数。而 LuaJIT 所做的正好相反,它并没有触发 throw,而是利用了隐藏的系统调用来模拟异常处理机制的行为。但就像任何表面一样,它只是一个模仿,不是真正的东西。

想象一下,C++ 代码 "throw X();" 被转换为以下代码:

auto thrown = x();
auto handler = find_handle_for_exception(thrown);
if(!handler) std::terminate();
auto except = allocate_exception(thrown);
handling_exception = true;
unwind_stack_to_handler(handler);
handling_exception = false;
handler(except); //将控制转移到句柄。

而 LuaJIT 则了解所有这些隐藏的内部系统调用。因此,当它想要 "throw" 异常时,它采取了以下步骤:

auto handler = find_handle_for_exception(); //只有匹配 ...
if(!handler) std::terminate();
unwind_stack_to_handler(handler);
handler(); //将控制转移到句柄。

在这个伪代码中,uncaught_exception 通过读取 handling_exception 的值来工作。但是糟糕的是,LuaJIT 没有在它的版本中更新那个变量。可能是因为它是 C++11 的新功能,他们从未在各种系统上检查过它的工作方式。或者可能在某些系统上确实有效,但在 MacOSX 上无效。

这总是一种危险,当您尝试在您的抽象提供的某些功能上进行仿真时,它可能无法以完全互操作的方式完成。我不知道为什么 LuaJIT 不能像正常人一样抛出真正的 C++ 异常...

2016-07-21 02:55:16