I didn't give much attention to the details. but I like the spirit very much.

LMB


On Wed, May 13, 2015 at 12:08 PM, Adam D. Ruppe via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
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!