The Right Approach to Exceptions

H. S. Teoh hsteoh at quickfur.ath.cx
Sat Feb 18 12:36:22 PST 2012


On Sat, Feb 18, 2012 at 08:20:23PM +0100, deadalnix wrote:
[...]
> However, in D, I think this is more the role of an Errors. Exception
> are something « softer ». It will alert you on problems your program
> encounter, but that are recoverable.

I agree.


[...]
> Let's get an exemple : your program ask a file to the user and do some
> operations with this file. If the file doesn't exists, you can prompt
> for another file to the user with a meaningful message and start
> again. However, the first version of your program can just ignore that
> case and fail with a less specific handler in firsts versions.
> 
> You cannot achieve something like that if you don't have a useful
> type to rely on. Here something like FileNotFoundException is what
> you want.

Exactly.  It's important to distinguish between exceptions sometimes.
For example:

	File promptSaveFile() {
		do {
			string filename = readInput();

			try {
				return File(filename);
			} catch(FileNotFoundException e) {
				writeln("No such file, please try "
					"again");
			}
		} while(true);
		assert(false);
	}

It would not make sense for that catch to be *any* Exception; for
example, if the exception was ReadFailureException, you do *not* want to
catch that, but you want to propagate it.

But sometimes, you *want* to handle ReadFailureException, for example:

	void salvageBadSector(out ubyte[] sector) {
		int n_attempts = 10;

		while(n_attempts > 0) {
			try {
				disk.read(sector);
			} catch(ReadFailureException e) {
				disk.recalibrate();
				n_attempts--;
				continue;	// try again
			}
			// If another error occurs, say OutOfMemory,
			// or some other unexpected problems, you do NOT
			// want to continue.
		}

		writeln("Cannot salvage data from bad sector");
	}

Having distinct exception types allows you to do this without resorting
to hacks, or bypassing the library altogether (because of the use of
overly generic exception types).

Having a proper exception class hierarchy is also necessary. For
example, most programs don't care about the difference between
FileNotFoundException and ReadFailureException; they just want to print
an error message and cleanup if I/O fails:

	Data loadData() {
		try {
			return readFromFile();
		} catch(IOException e) {
			writeln("I/O error occurred, aborting");
			cleanup();
			exit(1);
		}

		assert(false);
	}

It would be wrong to just catch Exception here, because if it wasn't an
I/O error, but something else, it needs to be propagated!


> The type of the exception must depend on the problem you are facing,
> not on the module that trhow it. I see a lot of people doing the «
> MyProgramException » or « MyLibException » but that doesn't make
> sense. In this case, you are just making things harder.

Agreed.


[...]
> If this politic is choosen, then It would make sense to have several
> modules of phobos throwing exceptions of the same type, or inheriting
> from the same base class.

Phobos NEEDS to have a sane exception hierarchy. ESPECIALLY since it is
the standard library. So you cannot assume your users cares or don't
care about distinguishing between exception types. Some applications
just catch Exception, cleanup and exit, but other applications need to
know the difference between FileNotFoundException and
ReadErrorException.

So we need to design a good exception hierarchy for Phobos.

The worst thing is if Phobos exceptions are too generic, then
applications that need to tell between different problems will have to
bypass Phobos and hack their own solution. Which is not good. The
standard library should be maximally useful to the extent that it can
be.


> Exception type is a convenient way to filter what you catch and what
> you don't know how to handle at this point.

Agreed.


T

-- 
The right half of the brain controls the left half of the body. This
means that only left-handed people are in their right mind. -- Manoj
Srivastava


More information about the Digitalmars-d mailing list