[Haskell-cafe] C++ exception (from *.so) catchable by Haskell??

Peter Simons simons at cryp.to
Sat Feb 15 12:45:44 UTC 2014


Hi Nick,

I have to admit that reading your messages has left me wondering what exactly
it is that you are trying to do? What exactly is the problem that you are
trying to solve?

 > I see the solution isn't as easy as just replacing e.g.
 >
 > void myUnexpected () {
 >   std::cerr << "unexpected called\n";
 >   throw 0;
 > }
 >
 > by
 >
 > void myUnexpected () {
 >   std::cerr << "unexpected called\n";
 > }

I'm not sure whether the purpose of std::unexpected_handler() has become clear.
That function is not supposed to handle exceptions in the sense that it makes
the exception "go away" so that normal program flow can continue. Its purpose
is to translate an unexpected exception -- which the program cannot handle --
into a different one that the program can handle. Maybe this quote from [1]
clarifies the matter:

 | A user-defined std::unexpected_handler is expected to either terminate the
 | program or throw an exception. If it throws an exception, one of the
 | following three situations may be encountered:
 |
 |   1) the exception thrown by std::unexpected_handler satisfies the dynamic
 |      exception specification that was violated earlier. The new exception is
 |      allowed to escape the function and stack unwinding continues.
 |
 |   2) the exception thrown by std::unexpected_handler still violates the
 |      exception specification:
 |
 |       a) however, the exception specification allows std::bad_exception: the
 |          thrown exception object is destroyed, and std::bad_exception is
 |          constructed by the C++ runtime and thrown instead.
 |
 |       b) the exception specification does not allow std::bad_exception:
 |          std::terminate() is called.

This means that the second myUnexpected() function from above is actually
illegal in this context, because it neither throws an exception nor does it
terminate the program.


 > The test code I have chosen for learning about the problem is hsqml by Robin
 > Kay, which I consider to be very well done, usually injecting into
 > HsQMLManager.{cpp|h}.hsqml_init(...),
 >
 > extern "C"
 > void hsqml_init(void (*freeFun)(HsFunPtr), void (*freeStable)(HsStablePtr) ) throw() {
 >   std::set_unexpected (myunexpected);
 >   throw runtime_error("OOOPS...");
 >   ...
 > }

I am not sure what exactly the intention behind that code snippet is. I've
looked into the hsqml source at [2], and the hsqml_init() function there
doesn't look like that.


 > What puzzles me currently is that, by the descriptions, I would expect
 > that terminate() actually is replaced by std::cerr, so that the
 > termination displayed in the result happens afterwards – but where??

I am not sure what you mean by that statement. std:cerr is an object, so it
cannot replace std::terminate(), which is a function.


 > In regard of what's my question, there should be something like a
 > common/best practice, shouldn't it?

Your question is how to integrate C++ code that might throw exceptions into
Haskell via FFI, right? Please correct me if that's not the case.

There is no one true way to accomplish that. Since you cannot catch or throw
C++ exceptions in Haskell, you'll probably have to pass them from C++ to
Haskell manually as a value. For example, the C++ function

  int do_stuff() { ... }

would need a wrapper like this one:

  enum exception_type { none = 0, runtime_error, logic_error, unknown };

  struct either_exception_or_int
  {
    exception_type err;
    int val;
  };

  void do_stuff_(either_exception_or_int * result) throw()
  {
    try
    {
      result->err = none;
      result->val = do_stuff();
    }
    catch(std::runtime_error const &)   { result->err = runtime_error; }
    catch(std::logic_error const &)     { result->err = logic_error; }
    catch(...)                          { result->err = unknown; }
  }

Now, on the Haskell side, you'd import do_stuff_() via FFI, and then write
another wrapper

  do_stuff :: IO Int
  do_stuff = alloca $ \result -> do
    do_stuff_ result
    val <- peek result
    either throwCxxError return val

  throwCxxError :: CxxExceptionType -> IO a
  throwCxxError Runtime_error = throwIO ...
  throwCxxError ...

to throw an appropriate Haskell exception that corresponds to the C++ exception
type.

I'm not sure whether anybody has done something like that before, i.e. I cannot
refer you to a concrete Haskell project that demonstrates this technique with
real-world code.


 > As Alexander states, there is a pleasing zero runtime approach with C++
 > exceptions.

I thought Alexander said the exact opposite, i.e. that C++ does *not* have zero
overhead exceptions?

Best regards,
Peter


[1] http://en.cppreference.com/w/cpp/error/unexpected_handler
[2] http://hackage.haskell.org/package/hsqml-0.2.0.3/src/cbits/HsQMLManager.cpp



More information about the Haskell-Cafe mailing list