Thread overview
Any way to create derived classes from Exception with canned messages?
Jul 31, 2021
Jeremy T. Gibson
Jul 31, 2021
jfondren
Jul 31, 2021
Adam D Ruppe
Jul 31, 2021
Adam D Ruppe
Jul 31, 2021
Jeremy T. Gibson
July 31, 2021

Is there any way to go about making derived Exception classes which produce a passed error message plus a hard-coded annotation string provided by the derived class, allowing me to use both a consistent error message suffix plus a programmer-specified error message?

I'm running into a problem where I want to create a set of Exception classes that automatically annotate Exception calls based on the class of the Exception, ensuring a more rigid syntax, avoiding typos and/or having to remember to identify the .

In my (barebones) production code, I currently have:

throw new Exception("vec3.bearing (NotYetImplemented)");

but I would vastly prefer that to be:

throw new NotYetImplementedException("vec3.bearing");

which offers code completion, typo safety, consistency, a toolbox of recommended exception types if I ever stop being the sole programmer, etc. (Still nothing I can do to pull the name of the current function and include that automatically (I think?), since the best I've got is __LINE__ etc. But we can't all eat pure syntactic sugar or we'd die of code-abetes.)

Now, https://github.com/dlang/druntime/blob/master/src/object.d clearly expresses that the constructors of Exception are @nogc. Therein lies the problem: there is no way to use the ~ concatenation operator in a @nogc function. At least at my current level of understanding of D (versus my background in C# where this is trivial), I can't think of any other way to pass a pure/nogc pair of strings that won't trip up the safety parameters.

Just as a raw test case, I tried the following:

https://run.dlang.io/is/VmCKwV

(where classes derived from MyException would then override the annotation function accordingly, avoiding the @safe limitation of global/static variables) and, sure enough, it didn't work -- as I anticipated it wouldn't.

I do get the reason why the Exception class is @nogc, as you can't necessarily rely on the memory state when an Exception is thrown. If it wasn't memory-safe, it would be possible to enter a state where the Exception explaining why the program is failing won't be produced when the program fails because it too fails, making it even harder to figure out the problem. But given that the substantial majority of manually thrown exceptions do consist of program states that aren't out of memory, it also limits my usage case.

I'm going to be digging around Phobos to see if I can mishmash something out of the stringizing functions, but that seems convoluted when a string concatenation "intuitively" should work, even though it obviously doesn't.

Has anyone else worked through this problem before and come up with a more elegant solution? Am I just over-analyzing the problem?

July 31, 2021

On Saturday, 31 July 2021 at 08:25:56 UTC, Jeremy T. Gibson wrote:

>

Now, https://github.com/dlang/druntime/blob/master/src/object.d clearly expresses that the constructors of Exception are @nogc. Therein lies the problem: there is no way to use the ~ concatenation operator in a @nogc function. At least at my current level of understanding of D (versus my background in C# where this is trivial), I can't think of any other way to pass a pure/nogc pair of strings that won't trip up the safety parameters.

Just as a raw test case, I tried the following:

https://run.dlang.io/is/VmCKwV

annotation() should also be nothrow as it's called by the nothrow constructor.

Option #1: https://run.dlang.io/is/OBNfxq

use a limited @nogc string formatter from dub, with an instance buffer.

Option #2: https://run.dlang.io/is/Osoiyd

for such a simple format, do it manually.

Option #3: NotYetImplemented()

require the caller to call a non-@nogc function which can build a string and then throw/return a constructed exception.

July 31, 2021

On Saturday, 31 July 2021 at 08:25:56 UTC, Jeremy T. Gibson wrote:

>

Now, https://github.com/dlang/druntime/blob/master/src/object.d clearly expresses that the constructors of Exception are @nogc.

That doesn't mean your constructors have to be! You can do whatever you want. Constructors aren't an inherited interface.

July 31, 2021

I'll add just for info sake even though you can just use normal gc in your own constructors as wanted, other options include:

  1. making a buffer inside your new object and copying the message to it, then passing a slice of your buffer to the super ctor.

  2. making all the messages compile-time literals so each class has a static message. then there's no runtime work at all. (and you can differentiate things based on type!)

can even be defined inline like

throw new NotImplemented!"thing"

and then the impl is like

class NotImplementedBase : Exception {
mixin ExceptionCtors;
}

class NotImplemented(string msg) : NotImplmentedntedBase {
this() { super ("Not implemented" ~ msg); }
}

that kind of thing just plus the file line info etc too arguments.

  1. ignore hte msg member and just override toString

But again the base ctor limitations do NOT apply to derived classes so it is all moot.

btw for virtual interfaces base things do apply but you're allowed to tighten if you want. so like base toString is NOT @nogc but if you wanted derived @nogc that's perfectly allowed. virtual methods must be as strict or stricter since they're inherited. just again ctors are not inherited so there's no connection at all.

July 31, 2021

On Saturday, 31 July 2021 at 11:30:06 UTC, Adam D Ruppe wrote:

>

On Saturday, 31 July 2021 at 08:25:56 UTC, Jeremy T. Gibson wrote:

>

Now, https://github.com/dlang/druntime/blob/master/src/object.d clearly expresses that the constructors of Exception are @nogc.

That doesn't mean your constructors have to be! You can do whatever you want. Constructors aren't an inherited interface.

Hah! I knew it was something simple. I should have guessed, when I was being shouted at when I tried to use the "override" keyword (obviously they're not virtual either), that I was approaching it all wrong.