Unwrapping the Beauty

Over the years, I've developed a habit of error-checking every single function call that could go wrong. Yes, this can easily make your code cluttered, but thankfully, you can create wrappers that perform both the call and error handling. That is, instead of calling fwrite() and checking for error a dozen times, you call myfwrite() that many times without any checks because its definition includes error check and termination. That's okay as long as it's a console program. But what if it's a GUI application that shouldn't terminate when the document could not be saved?

Writing a wrapper won't do any good in this situation. If you do, you'll have to error-check each call to the wrapper as well. So resorting back to error-checking every fwrite() call and immediately returning an error code to the GUI handler is how I'd usually do it. Like this:

on_mni_file_save():
  if save_document() != 0:
    show error

save_document():
  ...
  fwrite(), if failed, return -1
  ...
  fwrite(), if failed, return -1
  fwrite(), if failed, return -1
  ...
  fwrite(), if failed, return -1

  return 0

Recently I started adding XCF support to Vara, and I found myself in a similar situation. Reducing the number of fwrite() calls was not an option. But the logic getting lost in error checks was not something that I could stand either. Also, naive error-checking had the possibility of missing checks here or there in future. So I started thinking for a better solution.

I first thought about creating an object that wrapped fp (the file pointer) and an error flag, and passing it to an fwrite() wrapper that would check for as well as record errors. Yes, I would be calling this wrapper even after some error happens, but it's an overhead that's worth having. Then I thought, wait, fp itself has an error flag; why not check this inside the wrapper? But then I thought, wait again, doesn't fwrite() check this flag on its own before proceeding? (glibc's fwrite() does this, and others must as well.)

So the solutions in each iteration of thought were like this:

  1. Create an object that wraps fp and an error flag, and then write a wrapper for fwrite() that would check as well as update this object in every call.
  2. Create an fwrite() wrapper that checks ferror() before proceeding. So no custom object.
  3. Just call fwrite(). No custom object, no fwrite()wrapper.

In all these cases, the error check inside save_document() happens in the very end. Of course, we'll be issuing more fwrite() calls this way when an error occurs, but it's better than making the code a mess with all those error checks. Also, if there's an fwrite() call inside a loop or something, you can add checks just in those portions to avoid unnecessary calls.

So this is how the modified code would look like:

on_mni_file_save():
  if save_document() != 0:
    show error

save_document():
  ...
  fwrite()
  ...
  fwrite()
  fwrite()
  ...
  fwrite()

  return 0 if fp has no error, -1 otherwise

I'm really glad that I spent a couple more minutes to think instead of jumping on top of the keyboard to write hundreds of lines of abstractions right away.

Note: although we are calling fwrite() a lot, no unnecessary switch to the kernel mode is happening since system calls are issued inside fwrite() only after checking for errors.

Read more from Nandakumar at nandakumar.org/blog/