c++ - 为什么 LuaJIT 抛出异常时 std::uncaught_exception 返回 false?

标签 c++ exception luajit

LuaJIT 手册 states :

Lua errors can be caught on the C++ side with catch(...). The corresponding Lua error message can be retrieved from the Lua stack.

这按预期工作 - 除了 std::uncaught_exception() 在这种情况下不返回 true。

以下是说明该问题的最小示例。 checker 的析构函数应在堆栈展开期间执行,因此其中的 std::uncaught_exception() 应返回 true,但事实并非如此。

怎么会这样呢?我是否误解了该过程,或者 LuaJIT 是否以粗略的方式执行异常引发?

struct checker {
    ~checker() {
        if(std::uncaught_exception()) {
            // condition should evaluate true, but doesn't
        }
    }
}

auto l = luaL_newstate();
try {
    {
        checker c;
        luaL_checknumber(l, -1); // this line causes LuaJIT to raise an error
    }
}
catch(...) {
    // this will be executed, as intended
    auto err = lua_tostring(state, -1); // read the LuaJIT error, works too
    // ...
}

最佳答案

原始 Lua(不是 LuaJIT)使用 C 风格的异常(长跳转),而不是 C++ 异常。 grep LUAI_THROW 的 Lua 源代码并注意 C/C++ 异常处理之间的差异。

/*
@@ LUAI_THROW/LUAI_TRY define how Lua does exception handling.
** CHANGE them if you prefer to use longjmp/setjmp even with C++
** or if want/don't to use _longjmp/_setjmp instead of regular
** longjmp/setjmp. By default, Lua handles errors with exceptions when
** compiling as C++ code, with _longjmp/_setjmp when asked to use them,
** and with longjmp/setjmp otherwise.
*/
#if defined(__cplusplus)
/* C++ exceptions */
#define LUAI_THROW(L,c) throw(c)
#define LUAI_TRY(L,c,a) try { a } catch(...) \
    { if ((c)->status == 0) (c)->status = -1; }
#define luai_jmpbuf int  /* dummy variable */

#elif defined(LUA_USE_ULONGJMP)
/* in Unix, try _longjmp/_setjmp (more efficient) */
#define LUAI_THROW(L,c) _longjmp((c)->b, 1)
#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a }
#define luai_jmpbuf jmp_buf

#else
/* default handling with long jumps */
#define LUAI_THROW(L,c) longjmp((c)->b, 1)
#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a }
#define luai_jmpbuf jmp_buf

#endif

我不确定这是否可能,但是你可以尝试用C++编译Lua,这将允许你捕获Lua异常。 注意,编译为 C 代码的原始 Lua 不支持 C++ 堆栈帧展开!

至于LuaJIT,它看起来像它自己实现了帧展开,请参阅lj_err.c以获取更多信息。这就是为什么在此过程中可能无法设置某些 CRT 变量的原因。

/*
** LuaJIT can either use internal or external frame unwinding:
**
** - Internal frame unwinding (INT) is free-standing and doesn't require
**   any OS or library support.
**
** - External frame unwinding (EXT) uses the system-provided unwind handler.
**
** Pros and Cons:
**
** - EXT requires unwind tables for *all* functions on the C stack between
**   the pcall/catch and the error/throw. This is the default on x64,
**   but needs to be manually enabled on x86/PPC for non-C++ code.
**
** - INT is faster when actually throwing errors (but this happens rarely).
**   Setting up error handlers is zero-cost in any case.
**
** - EXT provides full interoperability with C++ exceptions. You can throw
**   Lua errors or C++ exceptions through a mix of Lua frames and C++ frames.
**   C++ destructors are called as needed. C++ exceptions caught by pcall
**   are converted to the string "C++ exception". Lua errors can be caught
**   with catch (...) in C++.
**
** - INT has only limited support for automatically catching C++ exceptions
**   on POSIX systems using DWARF2 stack unwinding. Other systems may use
**   the wrapper function feature. Lua errors thrown through C++ frames
**   cannot be caught by C++ code and C++ destructors are not run.
**
** EXT is the default on x64 systems, INT is the default on all other systems.
**
** EXT can be manually enabled on POSIX systems using GCC and DWARF2 stack
** unwinding with -DLUAJIT_UNWIND_EXTERNAL. *All* C code must be compiled
** with -funwind-tables (or -fexceptions). This includes LuaJIT itself (set
** TARGET_CFLAGS), all of your C/Lua binding code, all loadable C modules
** and all C libraries that have callbacks which may be used to call back
** into Lua. C++ code must *not* be compiled with -fno-exceptions.
**
** EXT cannot be enabled on WIN32 since system exceptions use code-driven SEH.
** EXT is mandatory on WIN64 since the calling convention has an abundance
** of callee-saved registers (rbx, rbp, rsi, rdi, r12-r15, xmm6-xmm15).
** EXT is mandatory on POSIX/x64 since the interpreter doesn't save r12/r13.
*/ 

附注我认为您已经了解安全的 Lua 类型检查 (lua_is*) 和安全函数,例如 lua_pcall

关于c++ - 为什么 LuaJIT 抛出异常时 std::uncaught_exception 返回 false?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38493718/

相关文章:

c++ - 是否可以将 c++ 命名空间中的函数与 luajit ffi 一起使用?

python - 如何捕获使用 python subprocess.checkout() 调用调用的 C++ 程序中发生的异常?

c++存储超出数组范围的值

c++ - 使用英特尔线程构建模块 (TBB) 的 Parallel_Scan 组件未实现加速

java - ClassNotFoundException 和 NoClassDefFoundError 有什么区别?

c# - ExecuteReader 上的 Mysql 异常

java - 使用 Spring AOP 捕获异常后不返回值

lua - 访问 torch 神经网络的内部神经元

c++ - namespace 标识符的范围是什么?