lua-users home
lua-l archive

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


On 4/18/06, Mike Pall <mikelu-0604@mike.de> wrote:\
> Greg Falcon wrote:
> > I really like Lua's approach to C/C++.  The benefit of using of
> > try/catch for errors in C++ is easily misunderstood.  Its benefit is
> > not that it will catch unhandled exceptions, and its use is more than
> > just a "let's use this because it's there" measure.
[...snip...]
> Just assuming you can throw an exception through the Lua core
> without affecting its internal state is bound to break in special
> circumstances or in future Lua versions.

But the benefit I'm talking about has nothing to do with throwing
exceptions THROUGH the Lua core.  Here is a snippet of C++ which
demonstrates what I think is useful about compiling lua as C++.  (I
just threw this together to get the point across, so sorry for any
syntax errors, etc)

  // Function defined elsewhere which reads from a file and may call lua_error
  void DoSomethingWith(lua_State* L, std::ifstream& file);

  int LuaDoFileThing(lua_State* L) {
    std::ifstream file;
    file.exceptions(ifstream::eofbit | ifstream::failbit | ifstream::badbit);
    try {
      file.open("test.txt");
      while (!file.eof())
        DoSomethingWith(L, file);
    }
    catch (std::ifstream::failure e) {
      lua_pushstring(L, "Error handling file");
      lua_error(L);
    }
    file.close();
    return 0;
  }

Here LuaDoFileThing is a Lua C++ function.  I see two main benefits:
1. If a file exception is thrown, either from file.open() or from
inside DoSomethingWith(file), it is converted to a Lua error in one
place.
2. If DoSomethingWith(file) calls lua_error, the stack is properly
unwound and the file object is destructed, avoiding resource leaks

Using compiled-as-C Lua binaries from C++ (using lua.hpp / extern "c"
wrappers) removes benefit #2.  The C++ standard explicitly states that
setjmp/longjmp is not guaranteed to call destructors of objects as it
forcibly unwinds the stack.

My fear of using stack-swapping tricks is different.  Does the
ucontext interface properly handle try/catch blocks?  The
documentation I've been able to find doesn't say.  I'm afraid that, at
least on some platforms, it will mess up try/catch blocks, and then
either other coroutines will incorrectly behave as though there was an
active ifstream::failure exception handler, or that this coroutine
will be incorrectly resumed with a broken or non-functional
ifstream::failure exception handler.

These concerns really depend on how important portability is as a
goal.  If I was developing a program to run on just a small handful of
platforms, then I'd test Coco's effect on try/catch handlers and would
use it if things worked.  More to the point, I'd use LuaJIT in that
case, as well.  But it depends on your goals.  Right now, for me,
portability trumps elegance.

A few code correctness notes:

IMO, Lua really ought to be fixed to use catch(struct lua_longjmp)
rather than catch(...), since lua_longjmp is the only type of object
Lua will throw.  Lua has no business catching other exceptions. 
Better to let unhandled exceptions remain unhandled.

The Resumable VM patch is not quite 100% portable ANSI C yet, as it
assumes an int can be stored in a void* and retrieved as an int later.
 The C standard says casting an int to void* can generate a trap
representation (6.3.2.3 p5) which can cause undefined behavior later
when the void* ctx is read (6.2.6.1 p5).

Greg F