当LuaJIT抛出时,为什么std :: uncaught_exception返回false?

时间:2016-07-21 02:08:29

标签: c++ exception exception-handling luajit

LuaJIT手册states

  

使用catch(...)可以在C ++端捕获Lua错误。可以从Lua堆栈中检索相应的Lua错误消息。

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

以下是说明问题的最小示例。应该在堆栈展开期间执行checker的析构函数,因此std::uncaught_exception()中的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 // ... } 应该返回true,但它不会返回true。

这怎么可能?我是否误解了这个过程,或者LuaJIT是否以粗略的方式执行异常提升?

scala> def fail(x: Int) = 1 -> x + 1
<console>:10: error: type mismatch;
 found   : Int(1)
 required: String
       def fail(x: Int) = 1 -> x + 1
                                   ^

2 个答案:

答案 0 :(得分:1)

原始Lua (不是LuaJIT)使用非C ++异常的C样式异常(long jumps)。 grep Lua为LUAI_THROW提供了资源,并注意到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.
*/ 

P.S。我想你已经知道安全的Lua类型检查(lua_is*)和安全功能,如lua_pcall

答案 1 :(得分:1)

std::uncaught_exception是C ++异常处理机制的一个功能。 LuaJIT正在做什么不是 C ++异常处理机制。 LuaJIT没有发出throw。它只是使用隐藏的系统调用来模仿异常处理机制的行为。但就像任何外观一样,它只是模仿而非真实。

想象一下,C ++代码&#34;抛出X();&#34;转换为以下内容:

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); //Transfers control to handler.

LuaJIT出现了,它知道所有这些隐藏的内部系统调用。所以当它想要&#34;扔&#34;例外,它是这样做的:

auto handler = find_handle_for_exception(); //Only matches ...
if(!handler) std::terminate();
unwind_stack_to_handler(handler);
handler(); //Transfers control to handler.

在此伪代码中,uncaught_exception通过读取handling_exception的值来工作。但是woopie,LuaJIT并没有在其抛出版本中更新该变量。可能是因为它是C ++ 11的新手,他们从不费心去检查这部分是如何在各种系统上运行的。或者它可能在某些系统上运行,但不适用于MacOSX。

当您尝试模拟抽象提供的某些功能时,这总是一种危险。你没有以完全可互操作的方式做到这一点。我不知道为什么LuaJIT不会像普通人一样抛出真正的C ++异常...