C++ exception (from *.so) catchable by Haskell??

Dear all, after trying around a while this seems to be a good question for Haskell-cafe to me: Is somewhere a place (e.g. some Hackage package) where intercepting exceptions from C++ library code is done, so one can look *how* it is done? Generally, it is the following: 1) Injecting code that throws a an exception in C++ library code, e.g., throw runtime_error("OOOPS..."); 2) then, I tried out everything in reach (Haskell docs, Hackage interfaces to C++ considered to be well-done) to get it caught (try, catch, bracket, finally; various exception types; forkOS). 3) To my surprise, every time the output stays the same: terminate called after throwing an instance of 'std::runtime_error' what(): OOOPS... Is it possible at all to get a grip of this? Is there some place where it is done? Does the terminate call already happen with the C++ execution? Any comment welcome, cheers, Nick

Two approaches:
1. Use set_unexpected.
http://www.cplusplus.com/reference/exception/set_unexpected/
2. Wrap your FFI code in try/catch.
Alexander
On Thu, Feb 13, 2014 at 12:43 PM, Nick Rudnick
Dear all,
after trying around a while this seems to be a good question for Haskell-cafe to me: Is somewhere a place (e.g. some Hackage package) where intercepting exceptions from C++ library code is done, so one can look *how* it is done?
Generally, it is the following: 1) Injecting code that throws a an exception in C++ library code, e.g.,
throw runtime_error("OOOPS...");
2) then, I tried out everything in reach (Haskell docs, Hackage interfaces to C++ considered to be well-done) to get it caught (try, catch, bracket, finally; various exception types; forkOS).
3) To my surprise, every time the output stays the same:
terminate called after throwing an instance of 'std::runtime_error' what(): OOOPS...
Is it possible at all to get a grip of this? Is there some place where it is done? Does the terminate call already happen with the C++ execution?
Any comment welcome, cheers, Nick
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

thank you :-) in regard of (1), I am still fiddling to get it running; as
Brandon did foresee, there seem difficulties here. Do have any piece of FFI
code in mind which makes use of this?
With respect to (2) – if one has a rather fine grained interface to rather
atomic actions, remains the overhead still OK at lots of function calls? I
read overhead should be quite low, but does it apply for FFI usage?
Cheers, Nick
2014-02-13 13:11 GMT+01:00 Alexander Kjeldaas
Two approaches:
1. Use set_unexpected. http://www.cplusplus.com/reference/exception/set_unexpected/
2. Wrap your FFI code in try/catch.
Alexander
On Thu, Feb 13, 2014 at 12:43 PM, Nick Rudnick
wrote: Dear all,
after trying around a while this seems to be a good question for Haskell-cafe to me: Is somewhere a place (e.g. some Hackage package) where intercepting exceptions from C++ library code is done, so one can look *how* it is done?
Generally, it is the following: 1) Injecting code that throws a an exception in C++ library code, e.g.,
throw runtime_error("OOOPS...");
2) then, I tried out everything in reach (Haskell docs, Hackage interfaces to C++ considered to be well-done) to get it caught (try, catch, bracket, finally; various exception types; forkOS).
3) To my surprise, every time the output stays the same:
terminate called after throwing an instance of 'std::runtime_error' what(): OOOPS...
Is it possible at all to get a grip of this? Is there some place where it is done? Does the terminate call already happen with the C++ execution?
Any comment welcome, cheers, Nick
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Thu, Feb 13, 2014 at 6:43 AM, Nick Rudnick
3) To my surprise, every time the output stays the same:
terminate called after throwing an instance of 'std::runtime_error' what(): OOOPS.
It's calling terminate which calls exit(), in the C++ runtime. You can't catch it at that level. set_unexpected() may or may not help you since it's not clear that you can make it pop back to the FFI call and no further. Maybe you can use setjmp()/longjmp() or setcontext() and friends, but I'm betting its interaction with C++ is undefined (and in particular C++ finalizers/destructors don't get called). Your best bet is to catch the exception in C++, in whatever code you are invoking from the (C context) FFI call. Upshot: there is no global concept of exceptions that applies across all languages unless you're using something like JVM or CLR. -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

thank you :-)
Injecting the example pointed to by Alexander to my code, e.g.
void myUnexpected () {
std::cerr << "unexpected called\n";
throw 0;
}
...
std::set_unexpected (myUnexpected);
try {
throw runtime_error("OOOPS...");
}
catch (int) { std::cerr << "caught int\n"; }
catch (...) { std::cerr << "caught some other exception type\n"; }
still is the problem that the STDERR message of myUnexpected doesn't appear
– is there anywhere code applying what you told about?
Cheers, Nick
2014-02-13 15:44 GMT+01:00 Brandon Allbery
On Thu, Feb 13, 2014 at 6:43 AM, Nick Rudnick
wrote: 3) To my surprise, every time the output stays the same:
terminate called after throwing an instance of 'std::runtime_error' what(): OOOPS.
It's calling terminate which calls exit(), in the C++ runtime. You can't catch it at that level.
set_unexpected() may or may not help you since it's not clear that you can make it pop back to the FFI call and no further. Maybe you can use setjmp()/longjmp() or setcontext() and friends, but I'm betting its interaction with C++ is undefined (and in particular C++ finalizers/destructors don't get called).
Your best bet is to catch the exception in C++, in whatever code you are invoking from the (C context) FFI call.
Upshot: there is no global concept of exceptions that applies across all languages unless you're using something like JVM or CLR.
-- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

On Thu, Feb 13, 2014 at 3:57 PM, Nick Rudnick
std::set_unexpected (myUnexpected); try { throw runtime_error("OOOPS..."); } catch (int) { std::cerr << "caught int\n"; } catch (...) { std::cerr << "caught some other exception type\n"; }
still is the problem that the STDERR message of myUnexpected doesn't appear – is
You're catching all exceptions so there are no "unexpected" ones to invoke myUnexpected on.
there anywhere code applying what you told about?
I'm not sure what you're asking here. My suggestion was essentially to have something like the try/catch you wrote for testing above wrapped around whatever C++ code is throwing the exception, and invoke that try/catch via the FFI. -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

2014-02-13 23:18 GMT+01:00 Brandon Allbery
On Thu, Feb 13, 2014 at 3:57 PM, Nick Rudnick
wrote: std::set_unexpected (myUnexpected); try { throw runtime_error("OOOPS..."); } catch (int) { std::cerr << "caught int\n"; } catch (...) { std::cerr << "caught some other exception type\n"; }
still is the problem that the STDERR message of myUnexpected doesn't appear – is
You're catching all exceptions so there are no "unexpected" ones to invoke myUnexpected on.
I experimented with commenting out the try/catch wrapping (I am afraid I did not make it clear; btw. the latter doesn't need set_unexpected). So my report refers to (only) std::set_unexpected (myUnexpected); throw runtime_error("OOOPS...");
there anywhere code applying what you told about?
I'm not sure what you're asking here. My suggestion was essentially to have something like the try/catch you wrote for testing above wrapped around whatever C++ code is throwing the exception, and invoke that try/catch via the FFI.
Oh, I am reworking an FFI interface to C++, and thought it would be neat to give it a nice exception handling, too. As you appeared quite experienced and I consider this to be a practical issue occurring not too seldom, I hoped you might refer to some project, where C++ exception handling is realized. Thanks, Nick
-- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

On Thu, Feb 13, 2014 at 6:07 PM, Nick Rudnick
Oh, I am reworking an FFI interface to C++, and thought it would be neat to give it a nice exception handling, too. As you appeared quite experienced and I consider this to be a practical issue occurring not too seldom, I hoped you might refer to some project, where C++ exception handling is realized.
Then I was apparently insufficiently clear before, so let me try to be clearer here: The ONLY way to handle C++ exceptions is from C++. You can't handle them from Haskell, or even from C. -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

Hi Nick, please note that the handler set by std::unexpected() fires only if a function violates its exception specification, i.e. the function void foobar() throw() { throw 0; } would trigger std::unexpected_handler(), but void foobar() { throw 0; } does not, because there is no "unexpected" exception. Uncaught exceptions, on the other hand, trigger the function std::terminate(), which by default translates to std::abort(). Brandon Allbery pointed this out before, but it's probably worth repeating: there is no way to catch (or throw) a C++ exception in Haskell. The internal details of C++ exception handling are "implementation defined". There is no standard that defines how to implement exceptions, which means that you cannot even mix code from different C++ compilers if you want to handle exceptions. Mixing in Haskell code is probably next to impossible. Take care, Peter

Thanks Peter for that informative answer.
One unmentioned fact is that the exception mechanism in C++ is designed to
allow for implementations to have zero runtime cost. Basically exception
handlers can be found by matching the stack frames with tables, and
registers can be populated using basically debug info.
How this is done is at the ABI level, but the Itanium C++ ABI, which is the
one used for x86-* does this.
http://mentorembedded.github.io/cxx-abi/abi-eh.html
An interesting nugget I learned today reading up on the ABI is that the ABI
actually supports common lisp conditions, that is, restarting or continuing
a computation that is in an exceptional state is supported by the ABI, but
not by C++. I don't know of any language that supports zero-overhead
exceptions and conditions, but the support is already standardized. It
isn't unthinkable that some C++ implementation would have extensions to
hook into the exception handling mechanism before stack-unwinding happens.
Alexander
On Fri, Feb 14, 2014 at 12:38 AM, Peter Simons
Hi Nick,
please note that the handler set by std::unexpected() fires only if a function violates its exception specification, i.e. the function
void foobar() throw() { throw 0; }
would trigger std::unexpected_handler(), but
void foobar() { throw 0; }
does not, because there is no "unexpected" exception. Uncaught exceptions, on the other hand, trigger the function std::terminate(), which by default translates to std::abort().
Brandon Allbery pointed this out before, but it's probably worth repeating: there is no way to catch (or throw) a C++ exception in Haskell. The internal details of C++ exception handling are "implementation defined". There is no standard that defines how to implement exceptions, which means that you cannot even mix code from different C++ compilers if you want to handle exceptions. Mixing in Haskell code is probably next to impossible.
Take care, Peter
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Thank you, Peter :-)
The exception specification hint is helpful – now I got it :-) Here,
the exception specification stuff is useful...
OK, I can intercept now, but 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";
}
... is it here where Brandon comes in, as the message gets out, but the
whole still carries the original Exception thrown with it – like you can
shoot yourself by your own bullet before the adversary's one arrives you?!?
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...");
...
}
hsqml_init(...), again, is directly called from
Graphics/QML/Internal/BindCore.chs – solely (grep ascertained there is no
other call from the C/C++ code):
{#fun unsafe hsqml_init as hsqmlInit_
{id `HsFreeFunPtr',
id `HsFreeStablePtr'} ->
`()' #}
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??
In regard of what's my question, there should be something like a
common/best practice,shouldn't it? As I see, catching inside C++ isn't an
issue at all with coarse-grained interfaces, as the overhead doesn't
matter. But I expect there are also some experiences for the case of lots
of calls to do from Haskell to very small C++ jobs – how then? As Alexander
states (just incoming... ;-) there is a pleasing zero runtime approach with
C++ exceptions – great if it that be done via pointer addresses only (i.e.
'virtual Either', addresses that will either point to a 'Left' or 'Right'
instance, but without tag), so there might be no additional overhead...
(though things would go somewhat opaque for the Haskell side)
But is there a practice to do this? If not, would it not mean a certain
overhead to construct/destruct, e.g. Either, lots of time, together with
doing the appropriate conditionals?
If not, there is no issue to worry about. :-)
So my interest is a very practical one – so insn't there hope even in case
there is no standard covering it, as concrete FFI interfacing goes to a
fixed project, let's say Qt4? So experiences might have gathered...
Cheers, Nick
2014-02-14 0:38 GMT+01:00 Peter Simons
Hi Nick,
please note that the handler set by std::unexpected() fires only if a function violates its exception specification, i.e. the function
void foobar() throw() { throw 0; }
would trigger std::unexpected_handler(), but
void foobar() { throw 0; }
does not, because there is no "unexpected" exception. Uncaught exceptions, on the other hand, trigger the function std::terminate(), which by default translates to std::abort().
Brandon Allbery pointed this out before, but it's probably worth repeating: there is no way to catch (or throw) a C++ exception in Haskell. The internal details of C++ exception handling are "implementation defined". There is no standard that defines how to implement exceptions, which means that you cannot even mix code from different C++ compilers if you want to handle exceptions. Mixing in Haskell code is probably next to impossible.
Take care, Peter
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

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

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

Don't speculate. Write benchmarks for the safe and unsafe benchmark
approaches using criterion and find out. Honestly any db query will take
at least 100 microseconds and any exceptions overhead plus safe ffi
overhead is going to be negligible.
Point being : write benchmarks. And for ANY computation that ever takes
more than 1 microsecond, please use the safe ffi.
On Saturday, February 15, 2014, Nick Rudnick
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
participants (5)
-
Alexander Kjeldaas
-
Brandon Allbery
-
Carter Schonwald
-
Nick Rudnick
-
Peter Simons