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?

Reply via email to