2016-07-21 17 views
2

Das luajit Handbuch states:Warum gibt std :: uncaught_exception false zurück, wenn LuaJIT ausgibt?

Lua Fehler können mit catch (...) auf der C++ Seite gefangen werden. Die entsprechende Lua-Fehlermeldung kann vom Lua-Stack abgerufen werden.

Dies funktioniert wie vorgesehen - außer dass std::uncaught_exception() in diesem Fall nicht wahr zurückgibt.

Im Folgenden finden Sie ein minimales Beispiel zur Veranschaulichung des Problems. Der Destruktor von checker sollte während des Abwickelns des Stapels ausgeführt werden und somit sollte std::uncaught_exception() in ihm zurückgeben, wahr ist es aber nicht.

Wie kann das sein? Missverstehe ich den Prozess oder führt LuaJIT die Ausnahmeregelung auf eine skizzenhafte Art und Weise aus?

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 
    // ... 
} 
+0

Welche Plattform? – immibis

+1

@immibis OSX 10.11.5, 'Apple LLVM Version 8.0.0 (clang-800.0.31)' – Appleshell

+0

Uncaught Ausnahme ist auf der Suche nach einer C++ Ausnahme, während 'catch (...)' auch Lua Einsen entdeckt. – Yakk

Antwort

1

Original-Lua (nicht luajit) verwendet C-Stil Ausnahmen (lange Sprünge), die nicht ++ Ausnahmen C sind. grep Lua-Quellen für LUAI_THROW und beachten Sie den Unterschied zwischen C/C++ - Ausnahmebehandlung.

/* 
@@ 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 

Ich bin nicht sicher, dass es möglich ist, aber man kann versuchen, Lua mit C++ zu kompilieren und dies ermöglicht es Ihnen Lua Ausnahmen zu fangen. Hinweis, Original Lua kompiliert als C-Code unterstützt nicht C++ Stack Frames abwickeln!

Wie für LuaJIT es sieht aus wie es implementiert Rahmen abwickeln selbst, siehe lj_err.c für weitere Informationen. Aus diesem Grund können einige CRT-Variablen während dieses Vorgangs nicht festgelegt werden.

/* 
** 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. Ich denke, dass Sie bereits sicher sind, dass Lua-Typenprüfungen (lua_is*) und sichere Funktionen wie lua_pcall sicher sind.

1

std::uncaught_exception ist eine Funktion des C++ Ausnahmebehandlungsmechanismus. Was LuaJIT tut ist nicht der C++ - Ausnahme-Handling-Mechanismus. LuaJIT hat keine throw ausgegeben. Es verwendete einfach versteckte Systemaufrufe, um das Verhalten des Ausnahmebehandlungsmechanismus nachzuahmen. Aber wie jede Fassade ist es nur eine Nachahmung, nicht die reale Sache.

Stellen Sie sich vor, dass der C++ Code "throw X();" wird umgewandelt in:

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 kommt mit und es kennt alle diese versteckten, internen Systemaufrufe. Also, wenn es will, eine Ausnahme zu „werfen“, tut es dies:

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

In diesem Pseudo-Code, uncaught_exception Arbeiten den Wert von handling_exception lesen. Aber woopsie, LuaJIT hat diese Variable in seiner Version von werfen nicht aktualisiert. Wahrscheinlich, weil es neu in C++ 11 ist, und sie haben sich nie die Mühe gemacht zu prüfen, wie das Teil auf den verschiedenen Systemen funktioniert. Oder vielleicht funktioniert es auf einigen Systemen, aber nicht MacOSX.

Das ist immer eine Gefahr, wenn Sie versuchen, einige Funktionen zu emulieren, die Ihre Abstraktion bietet. Dass du es nicht perfekt interoperabel machst.Ich habe keine Ahnung, warum LuaJIT nicht einfach eine echte C++ Ausnahme wie normale Leute werfen könnte ...

+1

Schätzen Sie die Einsicht. Ich denke, ich muss mich an die Mailingliste wenden, um zu sehen, ob es möglich ist, 'std :: uncaught_exception' in einem zukünftigen Update zu berücksichtigen, nachdem ich dies auf anderen Plattformen getestet habe. (Nur eine Anmerkung, es sieht so aus, als wäre es nicht neu in C++ 11, sondern existiert seit C++ 98;) – Appleshell