[Haskell-cafe] C++ exception (from *.so) catchable by Haskell??
Carter Schonwald
carter.schonwald at gmail.com
Sun Feb 16 06:59:21 UTC 2014
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 <nick.rudnick at gmail.com> wrote:
>
>>
>> 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/091f55b5/attachment-0001.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/091f55b5/attachment-0001.gif>
More information about the Haskell-Cafe
mailing list