lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Feel free to correct me if I'm wrong, but I thought this is not valid use of the lua C api.

To my knowledge, compiling lua as C++ does NOT make it okay to throw exceptions through lua.

To my knowledge, compiling lua as C++ ONLY serves to make it so that if you raise lua error, e.g. by calling luaL_error, the longjmp is instead performed by an exception throw which is caught by lua.
This means that any local variables with nontrivial destructors in the function that called luaL_error will have their destructors called and you won't have UB. That's very helpful for C++ programs, but it's not "lua is a C++ library now and behaves like something you would find in boost". It's a very limited form of support that e.g. makes it possible to put nontrivial C++ objects in userdata without leaks, and not a lot more than that.

To my knowledge, in general you need to treat lua like it is an ANSI C library, which happens to compile as both C and C++ -- because that's what it is. Throwing your own exceptions through such a library is inherently broken. Such a program, written in the common subset of C and C++, has to swallow them because there's nothing else it can realistically do with them. And it doesn't simply translate them as an error code "lua_user_exception_thrown == -1" or whatnot because that would discard all relevant info and defeat the point of C++ exceptions, which are supposed to be structured error signals, which represent problems that are so serious that we need to drop everything to handle them *right now*. If you are writing a program where it seems like exception -> error code translation would be useful, most likely you are abusing / overusing C++ exceptions, which should only be used for truly exceptional situations which might warrant bringing down the program.

Citations:
[1] Related mailing list discussion (see responses also, especially Greg Falcon's response) http://lua-users.org/lists/lua-l/2007-10/msg00473.html
[2] See also http://lua-users.org/lists/lua-l/2010-03/msg00408.html
[3] Lua users wiki: http://lua-users.org/wiki/ErrorHandlingBetweenLuaAndCplusplus

The general advice is, think of lua as a C library. It's not nice to throw exceptions through a C library -- it can't in general handle them correctly. Did you ever perform the exercise of writing an exception-safe standard container, like vector? There's a pretty famous series of GOTW where Herb Sutter walks through the pitfalls. It's not trivial, and if you don't have many carefully placed `try` and `catch` blocks there's definitely no way you will succeed.

It's true that `LUAI_TRY` does have some try and catch blocks when we compile in C++ mode, but this macro is pretty limited and if these are the only try-catch blocks in lua (they are), it should be clear that it's not going to be fully strongly exception-safe.

So I don't think you should think of `LUAI_TRY` having a `try { ... } catch (...) { ... }` format as license to throw anything and everything into lua.

My interpretation is that the code you posted is bugged and falls outside the spec of lua. IMO it should be corrected to this:

// Compile Lua 5.3.3 as C++
// no extern "C" { ... }
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

int thrower(lua_State* L) {
    lua_pushnil(L);
    return lua_error(L);
}

int main() {

    lua_State* L = luaL_newstate();

    lua_pushcclosure(L, thrower, 0);
    int top_before = lua_gettop(L);
    int code = lua_pcallk(L, 0, 0, LUA_NOREF, 0, NULL);
    int top_after  = lua_gettop(L);

    return 0;
}

Would love to hear confirmation or corrections from developers, or alternative interpretations.

Chris

On Fri, Oct 14, 2016 at 7:45 PM, ThePhD <jm3689@columbia.edu> wrote:
I have a strange bug I came across while trying to help someone with an issue with Sol. Lua says it can compile as C++. So, I tried to compile the following with C++ and I get a strange error after throwing an error that gets caught by the LUA_TRY macro and then returned:

// Compile Lua 5.3.3 as C++
// no extern "C" { ... }
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

int thrower(lua_State* L) {
    throw 0;
}

int main() {

    lua_State* L = luaL_newstate();

    lua_pushcclosure(L, thrower, 0);
    int top_before = lua_gettop(L);
    int code = lua_pcallk(L, 0, 0, LUA_NOREF, 0, NULL);
    int top_after  = lua_gettop(L);

    return 0;
}


`code == -1`, which isn't a valid return for `lua_pcallk`, and `top_before == 1 `, and `top_after == 1` as well (the function is not cleaned off the stack).

It seems like a bug in the Lua implementation.

As a side note, C++ finally made warnings about `char*` -> `const char*` an error in recent versions of C++ and also still has implicit `void*` casts to other pointer type banned, so there are a few places in the code where compiling as a (recent) version of C++ will trigger an outright error, which are:

lobject.c, Line 255: char *pdot = strchr(s, '.');
Just add `const`: const char *pdot = strchr(s, '.');
Rest of the code seems to compile fine in C and C++ after this change

lstrlib.c, Line 936: char *ppoint = memchr(buff, point, nb);
Add a cast so that the line reads: char *ppoint = (char*)memchr(buff, point, nb);
Rest of the code seems to compile fine in both C and C++ mode after this change

Apologies for the long message, but I just wanted to explain everything that was happening.