The Right Approach to Exceptions

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Mon Feb 20 12:46:21 PST 2012


On 2/20/12 1:15 PM, H. S. Teoh wrote:
> I think what Andrei wants to do is more along these lines:
>
> 	catch(Exception e) {
> 		writeln(formatLocaleString(e.fmt, e.data));
> 	}

Appproximately, except the formatting string is not inside the exception.

> I think there's some merit to this idea. However, I'm still unsure about
> storing stuff in a Variant.
>
> For one thing, you either need some sort of format string in the
> exception object (as I have above), which is bad, as somebody else
> pointed out, because now you're mixing i18n code into exception code, or
> you need some way of figuring out what format to use for which
> exception. So ultimately, you'll still end up with with a huge switch
> statement,

no

> or a global table of all exceptions (not good for
> maintenance, now every time someone changes an exception he has to
> remember to update the table).

Tables (including database-store ones etc.) are a way of life in 
international applications.

> One solution, perhaps, is to have an i18n file containing mappings of
> exception types to message formats. So that you can have:
>
> 	class Exception : Error {
> 		Variant[string] info;
> 		...
> 	}
>
> 	string[string] exceptionFormats = loadLocaleData();
>
> 	string formatExceptionMsg(LocaleInfo li, Exception e) {
> 		if (typeid(e).name in exceptionFormats) {
> 			return format(exceptionFormats[typeid(e)],
> 				e.info);
> 		}
> 		return e.msg;
> 	}

That's quite sensible.

> This may be acceptable, if the catching code knows which formats are
> actually defined in the locale data, so it will only call
> formatExceptionMsg() if there's actually a format defined for it. This
> way, you don't need to have *every* exception translated, just those
> that your catching code will actually use.

Yah.

> The one objection I have with this, though, is that if the catching code
> wants to specifically catch, say, FileNotFoundException, and extract the
> offending filename from the exception object, it would have to do a
> string lookup in Exception.info, rather than just accessing
> FileNotFoundException.filename directly.

Speed of rendering exception messages is secondary, but point taken.

> Then if the Phobos maintainer renames the field, the code still compiles
> since the compiler has no way to know that Exception.info["filename"] is
> no longer set by the ctor of FileNotFoundException, so the problem will
> go undetected until the exception actually occurs, at which time the
> catching code gets a runtime error (very bad).
>
> Having the field directly in FileNotFoundException is better, because
> you get a compile-time error when the field is renamed, so the developer
> can fix it before it ships, rather than have the customer run into the
> runtime error.

Yah, it also means there's a lot of duplicated code which may contain 
other bugs.

> That's why I proposed to use runtime reflection to scan the exception
> object for applicable fields. Then you get the best of both worlds: the
> message formatter doesn't need to know what the fields are, and you get
> full compile-time type checking for catching code that directly accesses
> the fields.

Runtime reflection is indeed a more general realization of the 
Variant[string] thingie. A naming convention (e.g. prefix all members in 
Exception relevant to rendering with "info" would complete a nice scheme.


Andrei


More information about the Digitalmars-d mailing list