News for hackers. Stuff that matters.
Re:The Elegance of Deflate by flohofwoe on Monday August 22, 2016 @02:45AM (#12334512)
Anyone know what's the history behind that setjmp/longjmp as 'poor man's exception' error handling? Since C works perfectly fine without exceptions, why add such a hack? I've seen this in libpng and I think libjpeg has it too (one of many reasons I'm avoiding those libs).
Re:The Elegance of Deflate by etatoby on Monday August 22, 2016 @07:15AM (#12335450)
If you have ever done any non-trivial coding in C, you will have come across the need (or the advantages) of using goto for local error handling within a function.

You can write the error handler code for a given function (which usually just resets some resources and returns an appropriate error value) in a single place, usually at the end, but jump to it from within any loop or structure within the function, as soon as you detect that the computation cannot proceed.

setjmp / longjmp is the natural extension of this mechanism to an entire library, as in a set of tightly coupled functions that are hopefully hidden behind a public API. I think it's a good design choice. If done properly it makes the code more readable, more easily maintainable, and more optimized for the most common execution path. I wouldn't avoid using those libraries just for that design choice.

Re:The Elegance of Deflate by johncolanduoni on Monday August 22, 2016 @03:02AM (#12334580)
C lacks "zero-cost" exceptions, where the code should be just as fast as it would be if no exceptions were involved as long as one isn't thrown. Although they are much more expensive when thrown than checking a return value and using indirection for any logical return values, they are a bit cheaper when you don't expect the exception to happen very often, or don't care about performance if it does (e.g. a corrupt png or jpeg file).

My dream is something like the UX of Swift's exceptions (basically normal C error handling except for it does all the tedious pointer work for you) with the ability to tell the compiler to either implement the exceptions as return values or stack unwinds for a particular code path with the flick of a switch.

Re:The Elegance of Deflate by vog on Monday August 22, 2016 @04:26AM (#12334857)
My big hope in this regard is Rust, which has a very good track record if implementing good, usable, zero-cost abstractions.

If they manage to implement zero-cost futures [1][2], maybe they will also implement sime kind of zero-cost error handling abstraction (either via exceptions, or via some other useful abstraction for that purpose).

[1] https://aturon.github.io/blog/2016/08/11/futures/

[2] https://news.ycombinator.com/item?id=12268988

Re:The Elegance of Deflate by barrkel on Monday August 22, 2016 @06:40AM (#12335330)
Rust's abstractions are only zero cost if you're looking in the wrong place for your costs. The Result / try! stuff in particular I would be very surprised if it is faster than a good exception approach, for example, particularly the happy path. It's amortised throughout though, while different exception throwing mechanisms have dramatically different performance, so people's intuitions aren't reliable.

I like Rust a lot. Zero costs is not a reason though; my reason is no GC and not as bad as C or C++.

Re:The Elegance of Deflate by steveklabnik on Monday August 22, 2016 @12:51PM (#12337616)
Don't forget exceptions aren't free themselves; you need landing pads, etc, all of which are not required.
Re:The Elegance of Deflate by barrkel on Monday August 22, 2016 @06:30AM (#12335293)
Transporting and checking for errors throughout the call graph definitely isn't free. Simple exception unwinding isn't very expensive either - store handler info on the stack and use stack frames and it's just a linked list search. Keep popping until you're done. Freeing locals in a way compatible with the language semantics along the way is the main tricky bit. But you have that cost with error results too, it's not extra.

The real cost is that exceptions force the compiler to be pessimistic and pedantically cope with exceptions everywhere. I really don't think there's much win at all, compared to that, in return values the compiler forces you to check. And if you skip the checking, that's a different kind of pain.

Checked exceptions is a different but related story. I think they're a fine idea in the small but appallingly bad in the large; when glueing together 30 different libraries with their own exception types, everything end up throwing anything, particularly when some higher level abstractions have been applied, like iterator streams or monads. Forced return code error checking like Rust's is isomorphic with checked exceptions. It is my biggest worry for the language.

That is, the problem isn't performance - the isomorphism shows that there is no necessary difference when rules are consistently applied - the problem is Java was a good demonstration the checked exceptions were misguided.

Re:The Elegance of Deflate by johncolanduoni on Monday August 22, 2016 @10:42AM (#12336628)
Rust's try macro will automatically use any available conversions when moving exceptions up the chain, so if you want to wrap exception A in B this can be done without pain. Rust also relegates a lot of what would have been exceptions in Java to panics, so that things like logic errors or issues with arguments can be kept from complicating error handling.

These alleviate a lot of what made Java's checked exceptions so unhelpful, so I don't think you can really conflate the two.

Re:The Elegance of Deflate by lmm on Monday August 22, 2016 @09:46AM (#12336267)
C doesn't work fine without exceptions. You have to write an enormous amount of boilerplate code to propagate error-returns correctly, or else an enormous amount of boilerplate duplication of your error-handling code. Or you use macros to reduce your boilerplate but it's difficult to do that in a way that handles different return types correctly, and once you get to the point of using a different return convention you're basically implementing your own language.
Re:The Elegance of Deflate by searealist on Monday August 22, 2016 @02:59AM (#12334567)
You don't have to use setjmp/longjmp in libpng, it's just the default behavior. You can instead set an error callback to do whatever you want.
Re:The Elegance of Deflate by chubot on Monday August 22, 2016 @05:10AM (#12334995)
I see setjmp/longjmp used a lot in recursive descent parsers in C.

I think people got tired of using up their return values for error codes. And it is more efficient to use this style certainly than manually breaking out of each stack frame.

C is full of things like this which break abstractions -- it doesn't seem particularly odd to me. Though I do remember that it was one of the things at the end of C books that I didn't understand for a long time.

What's the downside?

It doesn't play well with C++ because destructors have to be run at the end of every scope, but that doesn't apply to C.

And I guess because it mutates globals, it is a bit confusing in a threaded context.

Re:The Elegance of Deflate by gpderetta on Monday August 22, 2016 @10:02AM (#12336367)
> And I guess because it mutates globals, it is a bit confusing in a threaded context.

What globals?

Re:The Elegance of Deflate by chubot on Monday August 22, 2016 @02:23PM (#12338288)
My understanding is it works a bit like errno. errno is global that is set by the callee and then the caller can check it whenever, not necessarily right after the call or in the same function. But it's actually a thread-local, so it works fine in a threaded context, but is just a little odd to look at.
Re:The Elegance of Deflate by pjc50 on Monday August 22, 2016 @05:13AM (#12335012)
It's the only way to do a non-local return, otherwise you have to make sure you check every error return condition. That's part of why Rust forces the programmer to check all return values, I believe.