Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Emulating exceptions in C: a case study (sevko.io)
55 points by sevko on Aug 27, 2015 | hide | past | favorite | 27 comments


I think exceptions are very bad idea. Not nearly as bad as implicit nullability but comparable. Hacky emulations of exceptions... Well I don't want to be near such code.

But that was interesting read non the less, thanks OP.


I agree it was an interesting read. Haven't thought about setjmp and longjmp in many years.

>> I think exceptions are very bad idea.

There are many cases, API implementations for example, where I cannot think of a better solution. Packing result codes into returned data, using return values, using global state, using passed state buffers, all those approaches are worse, imo, and lead to less readable and maintainable code. What's your preferred alternative?


In Haskell I use Maybe's, Either's and their ilk (packing result code in returned data I guess?), in C - your usual boolean return values like this: bool parse_literal (ParsingState* state, LiteralValue* pResult).

In Java/C# I have to use what other people are using :)

My dislike of exceptions (as well as errno and Set/GetLastError) is due their masking, hiding error conditions and situations. And I prefer everything to be stated clearly. Including error handling code (answer to 'did I handled all possible failures of calling this?' should be instant).

Unfortunately proper error handling is hard, nearly as much as 'naming things' :)


> longjmp() and setjmp() are tricky. They’re obscure, can give rise to subtle bugs, are highly platform-specific, and, if abused, will probably lead to awfully confusing code

Actually, being part of the ISO C standard, they are not platform-specific. Otherwise, just use C++ where destructors take care of cleanup when an exception is thrown.


> Actually, being part of the ISO C standard, they are not platform-specific.

The standard doesn't define the full semantics, specially what happens when a signal, trap, OS exception and similar occurs.


Perhaps use them in production code only (where performance is critical), and use something else in debug/development mode?


This is like an article on how to use a screwdriver as a chisel.


If you need a chisel but only have a screwdriver then advice on how to best use it sounds valuable to me.


Everybody has access to C++ now. Even on the Arduino. If you need exceptions, go to C++. "longjmp" was a bad idea when it was first invented, and it hasn't improved with age.


Yet whole applications are written around it and they work perfectly well. Not saying that is an argument that it is somehow a good idea, just that while it might looks bad, it also works ok when done properly.


> it also works ok when done properly

That's circular reasoning. The objection is that it only works well in certain circumstances. Mostly in fairly simple applications with either small teams or rigid conventions to enforce clarity and prevent bugs.


Well, Lua coroutines uses longjmp and they're not "fairly simple" in any way.

I don't think it is a circular reasoning. There's always the good and the bad way to use something, even C++ exceptions.


If "done properly" implies "works ok", then it's circular reasoning.

I'm not sure your example is a counterexample either. Implementing part of a fairly simple runtime (like Lua's) using longjmp is an example of a good use of longjmp, in my opinion.


I've seen longjmp used in a number of fairly large projects, with diverse teams. E.g. postgresql.

If abstracted away they don't necessarily cause that many problems. Sure, it'd be nicer if there were a better alternative, but often there's just none. And no C++ isn't always an alternative. Many older platforms have only horribly bad C++ compilers, certainly not ones where you want to rely on exception handling.


> Everybody has access to C++ now.

No they really don't; sometimes you have no choice but to use C because you're using a framework or platform where that's the only option.


As if longjmp and c++ exceptions are the only two ways to go. How about return codes? Or error out parameters? Not so bad to work with if you be consistent about the style...



When I went for a job interview at Symbian many years ago for a position working on Symbian OS, they told me (and it's obvious from looking at their documentation) that they implemented thier own execption mechanism in C++ using setjmp and longjmp becuase they deemed that the C++ execption system was too slow. Other accounts say that the exception mechanism wasn't present in the language at the time, which I am not sure of.

This is why you had to push all local variables onto a clearnup list in each an every function, this I think, from memory was a call to CleanupStack::Push with a wrapper macro.

If indeed they did this becuase exxceptions were 'too slow', then the problem, im my option would be that they were using exceptions far too much -- as status indications and so on. They are, again, in my option a great help used in the right way, with the C++ RAII idiom, which is essential in the face of exceptions.


I actually wrote a JSON parser for fun and profit 2 weeks ago. It's mind blowing to me how his code is similar to mine. I wrote mine completely from scratch. I even called my routine `parseValue`. Once one has a look at the JSON grammar, writing a parser for it is trivial.


Exceptions are not about going from one place to another. It's about unwinding the stack properly and calling the destructors of variables on the stack. This is not a close emulation at all.


While I'm not saying this is a good idea, you can combine a pool allocator with longjmp exceptions to get an approximation of unwinding. It requires that you are very careful about structuring your code, but I once wrote a whole web server this way: http://git.annexia.org/?p=rws.git;a=tree (using: http://git.annexia.org/?p=c2lib.git;a=tree ).


But sometimes it's not just memory. It can be files you need to close, or any other resource held.


That too can be queued up, with a list of functions to call to free those resources.


He was referring to a memory pool allocator. But regardless, good luck with the approach.


Pool allocators can easily deal with non-memory resources. The one I wrote in c2lib could.


Let me use my favourite tool of intellectual terrorism: Just use monads. Ouch, now we have two problems here. :P


To be serious: This was my first thought, too. Exceptions are essentially one kind of monads, and any complex compiler or interpreter could make good use of more types of monads.

I believe the paper of Wadler makes this most clear. Some time ago, it was a real eye-opener for me:

http://homepages.inf.ed.ac.uk/wadler/papers/essence/essence....




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: