5 ms·
It depends on the problem you're trying to solve, I think. Let's consider a command line application that fetches a URL, like wget. Without exceptions, you wou
by RyanZAG 13y ago
It depends on the problem you're trying to solve, I think.
Let's consider a command line application that fetches a URL, like wget. Without exceptions, you would check the return code of all the system functions you call (dns, sockets, etc), and if any of them fail you can't really recover. You just write out an error message and exit. With exceptions, you could wrap the whole thing and if any system function throws an exception, you print out the exception and exit. So it's much the same in this case.
Now put the url fetch code into a larger application. With exceptions this is pretty easy - you can catch any exception thrown by your url fetch code and then retry it a few times until it succeeds, or you inform the user of the app. Without exceptions this is tougher as you need to unroll everything manually in order to retry.
Finally we have something complicated like running a simulation. If something gives an error, we probably don't want to stop the whole simulation. We'd want to check the specific error result and then fix the simulation and continue. In this case exceptions would add a lot of boiler plate code and would need to be handled just as carefully as error results (eg, wrapping each call in its own try and handling each error).
So it's pretty clear that it depends is very applicable to when you'd rather use exceptions or error results. In general I think exceptions are better as long as they're only used for actual error cases as it gives the user of the functions more control over where and at what level to handle exceptions.
- nightski 13y agoBut what if the exception was in your code and not the system call? This is incredibly dangerous. Monads are perfect for this. Really combines the best of both worlds without all the unspecified/undefined behavior.
- Groxx 13y agoThen it should be a different exception, and if it matters, you should be treating that class of exceptions differently. Sometimes it doesn't matter.
- memracom 13y agoYou could always write Monads in C... http://blog.sigfpe.com/2007/02/monads-in-c-pt-ii.html http://blog.sigfpe.com/2007/02/monads-in-c-pt-ii.html
- gngeal 13y ago> Now put the url fetch code into a larger application. With exceptions this is pretty easy - you can catch any exception thrown by your url fetch code and then retry it a few times until it succeeds, or you inform the user of the app. Without exceptions this is tougher as you need to unroll everything manually in order to retry. Unless you have something like goroutines - or Erlang-style processes - and perhaps employ a moderately disciplined coding style. Retrying those is just a matter of relaunching them, isn't it? Also, Common Lisp got exceptions mostly right. Many other languages...not so much, I guess.
- vidarh 13y ago> Without exceptions this is tougher as you need to unroll everything manually in order to retry. I don't get why you think this is a problem. You can trivially "unroll" it by explicitly writing your functions to be single-exit and "unrolling" at the end before returning whatever your result is. "Exception handling" without "real" exceptions is then also trivial: just check the appropriate error code and take whatever action is suitable at the time.
- tracker1 13y agoThis is why I really like the callback pattern that node.js tends to use for all it's IO api calls... the callback always has the option for an error first.. generally if its' a nested callback, bubble up early. Internally, the C code can just check the state and call the callback method early if an error state arises.
- yason 13y agoC libraries don't just exit. A C executable could in some circumstances just fprintf() to stderr and exit(1) but usually even that requires sane deinitialization so before exiting you will effectively end up back in main() from the lower levels. C libraries generally handle their errors on some level, possibly jumping directly out from the lowest levels and then doing deinit before passing back an error code to the caller of the library function, or exiting in a nested fashion by deinitializing stuff and reporting failure to the caller. Whatever works for whichever codebase but try to use any decently popular library, such as libcurl, and see if they just exit in the middle.
- alexkus 13y agoExactly. Writing libraries in C means you have to be much more strict than simple throwaway single execution binaries. What our company has ended up doing is wrappering most low level functions with the common code around it. For example, fopen() may fail and return EINTR if the process received a signal just as it was opening a file (we use signals to tell processes to reread their config so this does happen albeit rarely) so the wrapper calls fopen() in a do/while loop that repeats on EINTR (and a few others). It saves you from writing the same 50 odd lines each time you want to open a simply open a file. In an exception led world you'd still have to do the same thing, but with different syntactic sugar. You can't just fail and bubble up the exception without dealing with the few exceptions you must deal with and repeat, and you can't just repeat on every exception as most will just fail again and again. You end up writing code to do the same thing just in different styles. Checking the return values of every single function call, and dealing with it, can make the code verbose (one line of code and then 10+ lines dealing with errors) but it is worth it in the end for bullet proof programs. Also code where you have to undo all of the work already done in the function up until that point, which can get a little tedious, i.e.:- APP_RET makefoo( char *fname ) { char *foo,*bar; thingy_t *qux; size_t len; ASSERT( fname ); len = strlen( fname ); foo = malloc( len+1 ); if( foo == NULL ) { return( ENOMEM ); } bar = malloc( (2*len)+1 ); if( bar == NULL ) { free( foo ); return( ENOMEM ); } qux = malloc( sizeof( thingy_t ) ); if( qux == NULL ) { free( foo ); free( bar ); return( ENOMEM ); } ... return( APP_OK ); } It's the job of anything calling makefoo() to deal with the various errors it can return, but the idea is to return the minimum number of unique error codes as necessary to avoid proliferation of error codes to the higher and higher functions. Many calling functions will only really care about success or failure, and will just use the return code to log out the reason for the failure. The wrappers help deal with many situations; did fwrite() write all of the bytes we wanted or just some of them? Well, our wrapper around fwrite will handle short writes and repeat the call depending on the result of ferror().