On Sunday, 15 August 2021 at 16:23:25 UTC, Ali Çehreli wrote:
On 8/15/21 2:10 AM, Alexandru Ermicioi wrote:
>> This may be useful in some cases but in general, these
colatteral
>> exceptions don't carry much information and I don't think
anybody
>> looks at them. Usually, the first one is the one that
explains the
>> error case.
> That is just an assumption.
Agreed but it's based on hands-on experience as well as
exposure to these forums. :)
> There could be designs where original
> exception gets wrapped in another one
Wrapping is different and yes, it is useful. There have been
cases where I hit a ConvException which only tells me a
conversion failed. I do catch and augment it in outer contexts
to saying something similar ot "That happened while doing this".
> Regarding exception chaining, do you mean that it will
automatically get
> chained, even without explicitly passing it as constructor of
wrapping
> exception?
Yes. That's the functionality which isn't very useful because
the collateral exceptions are usually because of the main one:
Imagine the file system is full and a destructor cannot flush a
file. The destructor's error is not interesting in this case.
class Main : Exception {
this() {
super("Main failed");
}
}
class Dtor : Exception {
this() {
super("The destructor failed");
}
}
struct S {
~this() {
throw new Dtor();
}
}
import std.stdio;
void main() {
try {
auto s = S();
throw new Main();
} catch (Exception exc) {
stderr.writeln("Failed: ", exc.msg);
stderr.writeln("This failed too: ", exc.next.msg);
// (Of course, real code should stop when 'next' is null.)
}
}
That output contains two automatically chained exceptions:
Failed: Main failed
This failed too: The destructor failed
Ali
Do you see anything wrong with the following `emplace`-allocated,
RAII following exceptions:
```d
import std;
import core.stdc.stdlib;
class Main : Exception {
this() @nogc{
super("Main Failed");
}
}
class Dtor : Exception {
this() @nogc{
super("The destructor failed");
}
}
T heapAllocate(T, Args...)(Args args)@nogc{
auto size = __traits(classInstanceSize, T);
auto memory = malloc(size)[0 .. size];
auto instance = emplace!(T,Args)(memory, args);
return instance;
}
struct S {
~this()@nogc {
scope a = heapAllocate!Dtor();
throw a;
}
}
void main() @nogc{
try {
auto s = S();
scope a = heapAllocate!Main();
throw a;
} catch (Exception exc) {
printf("Failed: %s\n", cast(char*)exc.msg);
printf("This failed too: %s\n", cast(char*)exc.next.msg);
// (Of course, real code should stop when 'next' is null.)
}
}
```
Is this good enough for general use now? Any other drawbacks?