lua-users home
lua-l archive

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


We are using an approach similar to what Philipp comments in APRIL-ANN:

I think another safe way is to not use objects with non-trivial destructors in your binding functions (i.e. wherever `lua_error()` might be called), and transform all exceptions thrown by your application code into Lua errors.

We have a binding library which wraps every C++ call with try{}catch{} blocks, and every time we capture an exception we change the value of a flag, and when the value of the flag is true we transform the C++ exception into a Lua error. Something similar to this:


bool exception_thrown=false;
try {
  // here C++ call, potentially exceptions can be thrown
}
catch (whatever) {
  exception_thrown=true;
}
if (exception_thrown) {
  lua_pushstring(L, "some error");
  return lua_error(L);
}

The reason to use the flag variable is to execute the longjmp of lua_error() outside the C++ exception handler, otherwise you can end up with memory leaks.

The problem arises when you call from C++ a Lua function, you need to wrap the call always with a pcall in order to translate the Lua exception into a C++ exception.

Doing this way the library can be loaded in a Lua binary compiled in C, without C++ support.




Lua supports being compiled as C++, and once the compiler is
instructed to compile the code as C++, it will automatically use
throw/try/catch.

This, however, has a side effect: all of Lua's functions are then defined,
in C++ terms, as extern "C++". Which is, unfortunately, a problem when Lua
is linked statically [1] and then another C-language library using Lua
needs to be linked statically with the same application. Linking such a
library (let's call it X) with the rest of the application (containing Lua
compiled in the C++ mode) will fail, because the Lua API functions will be
decorated according to extern "C++", while X expects extern "C". It may be
argued that X could be recompiled as C++, but C and C++ are not fully
compatible, sometimes with run-time differences only [2], so this is
generally not advisable.

In a project where I had this problem, I resolved it by altering the
following definition in luaconf.h:

original:

#define LUA_API extern

modified:

#ifdef __cplusplus
#define LUA_API extern "C"
#else
#define LUA_API extern
#endif

As far as I can tell, nothing else is needed to resolve the issue. I
suggest that this be incorporated into Lua's official source.

You shouldn't throw exceptions from `extern "C"` functions, especially if real C code is involved. So the whole point of compiling Lua as C++ becomes moot. Also `extern "C" int (*)( lua_State* )` and `extern "C++" int (*)( lua_State* )` are distinct types not convertible to each other, and currently you can only pass one *or* the other to the Lua API functions, depending on how the Lua library was compiled. So if you have C code *and* C++ code, and both use `lua_CFunction`s, your only standards compliant option is to compile Lua as C and use `extern "C"` in your C++ code for your binding functions.


I have thought about another way of fixing this, but I am unsure about its
true merit. The try/catch/throw keywords, when compiled in the C++ mode,
are only used once each, and in a very simple way, without much dependency
on the rest of the library. Therefore, this C++ code can be extracted and
placed in a separate file, one with a C++ extension, and only be compiled
when the C++ error behaviour is needed. I do not think Lua needs to be
compiled as C++ for any other reason. Then the rest of library need only
support being compiled strictly as C, which may require less of an effort
to ensure C/C++ standard conformance. As stated above, I am not really sure
whether the benefit is real.

Real C code is incompatible with exceptions, so most Lua API functions will need to be `extern "C++"` anyway just because they could be on the call stack when an error/exception is thrown.


Cheers,
V.


Philipp