Let's improve D's exceptions
Adam D. Ruppe via Digitalmars-d
digitalmars-d at puremagic.com
Wed May 13 08:08:57 PDT 2015
Have you ever done:
if(something) {
import std.conv;
throw new Exception("some error " ~ to!string(some_value));
}
Don't you hate it?
* having to import std.conv to see data from your exception is a
pain
* it allocates eagerly and thus isn't suitable for a lot of places
* inspecting the data can be a pain as the string is unstructured
This assumes the data is even bothered to be added. Anyone who
has gotten a RangeError in D knows important information is often
just dropped!
A good solution is to make a new exception subclass for each
error type, storing details as data members. However, that's a
bit of a pain in D because of all the work you have to do to make
a complete subclass:
* you need to forward the constructors
* you need to reimplement the toString to print out data members
* you still have a string member there that is encouraged to be
used
Finally, with the discussion of @nogc exceptions, it would be
nice to simplify the constructor and get file/line info separated
from it at the same time too. Can we tackle all these problems at
once?
I propose we do some library changes, one minor compiler change,
and a culture shift toward better exceptions. Here's my proof of
concept:
http://arsdnet.net/dcode/exception.d
Read that code and the comments to see what I'm going for and
what it looks like. Here's the summary:
* The Exception constructor is simplified: no required arguments,
everything else is done with members. (The Throwable next = null
argument might come back, but it can also be set after
construction anyway so I'd prefer not to worry about it in most
places either.)
* The string is de-emphasized. It is still there so we don't
break all the code that uses it now, but it is no longer the main
thing and we really would prefer that it isn't used at all.
Exception messages are instead done with the class name and data
members. The string may be gotten by the class though in cases
like converting errno to a message.
* File and line is set at the throw point instead of the
construction point.
* A mixin template uses reflection to print data to the user
instead of constructing a string.
* This is allocation-free, except for the exception object
itself. User code would NOT have to import std.conv, it just
stores the data in the object.
Changes:
TO THE COMPILER:
* make the call to d_throw add file and line number
information. This is completely backward compatible and allows
the runtime to store it in the object if it wants to. Then my
"fly" method becomes unnecessary.
TO THE LIBRARY:
* Throwable's toString implementation changes a bit to call a
couple more virtual functions to print details. Very similar to
the current code, as you can see in my file there, just some
overridable hooks.
* A PrintMembers and perhaps other helper mixins are added to
the library. Doesn't matter where they go, druntime might do a
simpler one for its own needs and then Phobos offers a full
featured one for user code. Or whatever. It'd need to be a bit
more complex than my proof of concept to cover more data (at
least let it try calling toString on types too) but this is
already pretty useful as is.
TO USER CODE:
* We start using subclasses with data members instead of string
arguments, migrating to it incrementally and encouraging people
to write exceptions this way going forward.
* Note that this should be completely backward compatible, it
won't break old code, just encourage a new better way.
What do you all think? I encourage you to grab my example there
and play with it a bit to see how you like it too and see if you
can think of any improvements.
D's exceptions kinda suck right now but they don't have to!
More information about the Digitalmars-d
mailing list