[Haskell-cafe] C++ exception (from *.so) catchable by Haskell??
Nick Rudnick
nick.rudnick at gmail.com
Sun Feb 16 03:39:24 UTC 2014
>
>
>
> 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?
>
You exactly got the point (below) with "Your question is how to integrate
C++ code that might throw exceptions into Haskell via FFI, right?".
The only thing I am worried about is that, due to lacking experience, I
might introduce unnecessary runtime overhead, as performance is critical. I
don't want to disimprove.
> > 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.
What a delicate question... :-)) :-)) :-)) Better I choose my words
careful... Hmmm... certainly... In my case, it's failed DB queries that may
be caused by failed SQL queries in a backend behind the C++ code; and as
far as I can judge, everything is already handled neatly with no collateral
damage to be feared for – roughly, these C++ exceptions say that the SQL
code wasn't executed – in (a possible) interactive mode meaning merely
"please repeat that query correctly...".
But currently, this appears somewhat complicated by
(1) termination of the code
(2) quite terse messages (i.e., I am interested in better debug
capabilities, too)
By reading Alexander's link to
http://www.cplusplus.com/reference/exception/set_unexpected/, I got the
impression that there is reasonable chance there is a simple mechanism
behind set_unexpected(...) allows replacing some default code leading to
termination by arbitrary code – very attractive under these circumstances
–, and facing the question what's the meaning of 'illegal' was second to
this...
Anyway, this round went to Murphy – or at least I do not see a way to get
the termination avalanche stopped yet.
>
> > 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.
>
Certainly, as lines were injected by me (I guess I mentioned at 1st mail)
for learning about the problem – with hsqml being needed as a scaffold for
this.
>
> > 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.
>
Please excuse: s/std:cerr/std:cerr.../
>
> > 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.
>
Definitely, modulo performance/overhead.
>
> 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.
>
You exactly concretized the 'Either' approach sketched in my mail, though I
would have tried to
(1) get a union instead of a struct for `either_exception_or_int'
(2) use pointers for err/val.
What I am worried about is that exactly this might result in inappropriate
overhead.
This directly leads to the other suggestion of my mail; to, instead of a
union of 2 pointers, let `either_exception_or_int' be just a pointer –
somewhat opaque for the Haskell side, which still might pipeline it very
efficiently from here to there, while it would be possible to C++ to
resolve the pointer addresses.
So with my 'illegal' one, I see 3 approaches yet.
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?
>
I referred to his mail just incoming when I answered to you (2014-2-14
9:11, "One unmentioned fact is that the exception mechanism in C++ is
designed to allow for implementations to have zero runtime cost. ...") –
what he addresses makes me quite less worried that catching C++ exceptions
themselves in the FFI would introduce performance issues, than
wrapping/unwrapping with Either & Co.
Cheers, Nick
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/haskell-cafe/attachments/20140216/c28c32cf/attachment.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 33A.gif
Type: image/gif
Size: 581 bytes
Desc: not available
URL: <http://www.haskell.org/pipermail/haskell-cafe/attachments/20140216/c28c32cf/attachment.gif>
More information about the Haskell-Cafe
mailing list