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:
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?