2/04/2009

02-04-09 - Exceptions

I'm not really high on the whole Herb Sutter super-exception safe everything, but they are really nice for error handling some times. They have two big wins in my opinion (vs. just having error return values and checking with if statements) :

1. Consolidating error handling code and putting the error-handling code in the place that actually can respond to the error in a reasonable way.

2. Correct default behavior if you are lazy or screw up and don't handle the error - it automatically propagates out rather than just being silently ignored. Standard if-checking badly violates one of my guiding principles in coding : if you don't write the right code, it just silently breaks; I always want my programs to noisily scream when I don't write the right code.

Anyway, I'm thinking about this because handling Async IO errors is a huge pain. The big standard problem comes from file opens. The thing is, my "fopen" doesn't actually open the file, it just queues an open request that will get done at some time in the future. If the open fails (say because the file doesn't exist), you don't actually see that until you try to get a char off the file and it's no good.

With if-checking it looks like :


File = fopen();
// no need to check error here really because this did nothing

... do other work to give it some time

char c = fgetc(File);
// ! boom this can fail because the file didn't actually open

// so fgets is no good, you have to do :
char c;
EStat status = fgetc(File,&c);
if ( status == error )
   ... clean up file, back out work already done, christ

With exceptions it's a million times cleaner. You just say the File ops can throw various IOExceptions. One of those IOExceptions is "FileNotFound". That exception might be thrown in the fopen() or in the next fgetc() or whatever.

In fact with exceptions and class back-out you can also have it automatically undo the temp work you did :

try {

File = fopen();

class X;
X.DoStuff();

char c = fgetc(File); // this might throw if the open actually failed

X.Read(File);

... okay, all good. now :

World.Apply(X);

} catch

Sadly I can't use exceptions for various reasons (aside from all the practical problems with exceptions, lots of clients have them disabled, so there you go), but my god the error stuff for this async IO is so nasty. One way I'm handling it is by sort of simulating the exception code style without exceptions.

What I do is still use an fgetc() that just returns a char. If the file fails to open, fgetc() just returns zeros, and an error flag is set in the file. That way you can go ahead and write straight line code without checking errors, and then once in a while you can check the error flag. So the code looks like :

{
File = fopen();

class X;
X.DoStuff();

char c = fgetc(File); // this just sets File.error

X.Read(File);

if ( File.ok )
   World.Apply(X);
// else X will just be destructed and not be loaded into the world

}

Sadly it's not actually so neat in practice. In particular with many layers of wrapping, exceptions just do What You Want magically. With an error flag I have to be pushing it through manually all the time. A lot of my files are actually a raw disk file, wrapped in a double-buffer file layer, wrapped in an LZ decompress layer. Each layer has to push out the status to the parent, and errors can occur at any point in the code because of the async nature.

No comments:

Post a Comment