www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Exception chaining and collectException

reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
Code:
--------
	import std.exception : collectException;
	import std.stdio;
	class MyException : Exception { this() { super("MYMY"); } }
	void f() { throw new MyException(); }
	struct S
	{
	    ~this()
	    {
		auto e = collectException!MyException(f());
		writefln("Collected: %s (%s)", typeid(e).toString, e.msg);
	    }
	    void method()
	    {
		throw new Exception("Dumb error");
	    }
	}
	void main()
	{
	    try
	    {
		S s;
		s.method();
	    }
	    catch (Exception e)
	    {
		writefln("Caught: %s (%s) (next=%s)", typeid(e).toString, e.msg,
			 e.next ? e.next.toString : "null");
	    }
	}
--------

Expected output: collectException should collect MyException while "Dumb
error" is in transit, and the output should contain a "Collected:" line.

Actual behaviour: collectException doesn't work as advertised;
MyException gets chained to the Exception in transit, and the
"Collected:" line is never printed.

If we change the collectException call to:

	auto e = collectException!Exception(f());

then it *does* work as advertised: the MyException instance is not
chained, but is correctly collected by collectException.

Why???

The code in question is reduced from a larger project where the dtor
needs to do some non-trivial cleanup, but needs to ignore certain errors
that may occur during cleanup. So it needs collectException to catch
only certain subclasses of Exception, rather than all Exceptions.  But
the above inconsistent behaviour hampers this.

Investigating the implementation of collectException, it seems that all
it does is to use a try-catch block to catch an exception of the
requested type.  So the question becomes, why does the catch block *not*
catch the instance of MyException when another exception is in transit?!


T

-- 
Gone Chopin. Bach in a minuet.
Aug 17 2017
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
Chained exceptions are a good idea, but are more or less a disaster:

1. No other language does chained exceptions

2. Attempting to hammer D chained exceptions into other language schemes (such 
as C++) makes for lots of unfun hours attempting to decode undocumented
behavior 
in those other schemes

3. Makes D exceptions incompatible with other language exceptions and their 
infrastructure

4. Are incomprehensibly implemented (I defy anyone to explain how the test
cases 
in the test suite are actually supposed to work)

5. Are more or less incompatible with non-GC memory allocation

I'd like to remove them from D.

I recommend *not* designing any program that requires them.
Aug 17 2017
next sibling parent reply Don Clugston <edibleplutonium completelysafe.com> writes:
On Friday, 18 August 2017 at 03:31:38 UTC, Walter Bright wrote:
 Chained exceptions are a good idea, but are more or less a 
 disaster:

 1. No other language does chained exceptions

 2. Attempting to hammer D chained exceptions into other 
 language schemes (such as C++) makes for lots of unfun hours 
 attempting to decode undocumented behavior in those other 
 schemes

 3. Makes D exceptions incompatible with other language 
 exceptions and their infrastructure

 4. Are incomprehensibly implemented (I defy anyone to explain 
 how the test cases in the test suite are actually supposed to 
 work)
Well, I wrote them, so I can explain that. The problem is that the idea that you can form a "chain" of exceptions turns out to be naive. What if a chained exception needs to get chained to another chained exception? And that then needs to be chained to another exception? It forms a tree! That's why the test cases are so complicated. So to a large extent, this extremely obscure corner case destroys the elegance of the concept. Secondly, exception handling in windows is practically undocumented. Certainly it's not documented in a single place. When I began to implement it, I feared it might be impossible. There isn't any guarantee that exception chaining can actually be implemented on all platforms.
 5. Are more or less incompatible with non-GC memory allocation

 I'd like to remove them from D.

 I recommend *not* designing any program that requires them.
I invested quite a lot personally in implementing chained exceptions. But I agree with you. I was actually quite proud that I worked out the nasty corner cases during the initial implementation. As far as I can tell, problems with chained exceptions are not because of bugs and implementation issues, but because of problems with the concept itself. I think it's just a bit too clever.
Aug 18 2017
next sibling parent jmh530 <john.michael.hall gmail.com> writes:
On Friday, 18 August 2017 at 09:09:47 UTC, Don Clugston wrote:
 Secondly, exception handling in windows is practically 
 undocumented. Certainly it's not documented in a single place. 
 When I began to implement it, I feared it might be impossible. 
 There isn't any guarantee that exception chaining can actually 
 be implemented on all platforms.
I had actually tested the above on Windows with DMD 2.075.1 and got the expected behavior, not the buggy behavior.
 I invested quite a lot personally in implementing chained 
 exceptions. But I agree with you.
 I was actually quite proud that I worked out the nasty corner 
 cases during the initial implementation. As far as I can tell, 
 problems with chained exceptions are not because of bugs and 
 implementation issues, but because of problems with the concept 
 itself.

 I think it's just a bit too clever.
Do you mind pointing me in the direction of where chained exceptions are explained?
Aug 18 2017
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 8/18/2017 2:09 AM, Don Clugston wrote:
 I invested quite a lot personally in implementing chained exceptions. But I 
 agree with you.
 I was actually quite proud that I worked out the nasty corner cases during the 
 initial implementation. As far as I can tell, problems with chained exceptions 
 are not because of bugs and implementation issues, but because of problems
with 
 the concept itself.
 
 I think it's just a bit too clever.
Thanks for the explanation. When I decided to support Dwarf exceptions, I spent a lot of time trying to match that behavior. We've all invested lots of time in things that didn't pay off. We mustn't get trapped by the sunk cost fallacy: https://en.wikipedia.org/wiki/Sunk_cost and I'm glad you're ok with that!
Aug 18 2017
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On Friday, 18 August 2017 at 03:31:38 UTC, Walter Bright wrote:
 Chained exceptions are a good idea, but are more or less a 
 disaster:

 1. No other language does chained exceptions

 2. Attempting to hammer D chained exceptions into other 
 language schemes (such as C++) makes for lots of unfun hours 
 attempting to decode undocumented behavior in those other 
 schemes

 3. Makes D exceptions incompatible with other language 
 exceptions and their infrastructure

 4. Are incomprehensibly implemented (I defy anyone to explain 
 how the test cases in the test suite are actually supposed to 
 work)

 5. Are more or less incompatible with non-GC memory allocation

 I'd like to remove them from D.

 I recommend *not* designing any program that requires them.
If we are to remove them, what happens when exceptions would normally chain? -Steve
Aug 18 2017
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 8/18/2017 5:07 AM, Steven Schveighoffer wrote:
 If we are to remove them, what happens when exceptions would normally chain?
In C++, throwing an exception while unwinding is a fatal error. More information: https://stackoverflow.com/questions/130117/throwing-exceptions-out-of-a-destructor
Aug 18 2017
parent reply Nemanja Boric <4burgos gmail.com> writes:
On Friday, 18 August 2017 at 22:51:35 UTC, Walter Bright wrote:
 On 8/18/2017 5:07 AM, Steven Schveighoffer wrote:
 If we are to remove them, what happens when exceptions would 
 normally chain?
In C++, throwing an exception while unwinding is a fatal error.
Well, you still can throw it, but you're not allowed to let it escape the destructor (you need to catch them before they would chain). C++ also provides a way to inspect if you're in the middle of the stack unwinding caused by an exception, to make this a bit more controllable, and I would think we should provide the similar primitive: http://en.cppreference.com/w/cpp/error/uncaught_exception.
Aug 19 2017
next sibling parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Sat, Aug 19, 2017 at 08:58:51PM +0000, Nemanja Boric via Digitalmars-d wrote:
 On Friday, 18 August 2017 at 22:51:35 UTC, Walter Bright wrote:
 On 8/18/2017 5:07 AM, Steven Schveighoffer wrote:
 If we are to remove them, what happens when exceptions would
 normally chain?
In C++, throwing an exception while unwinding is a fatal error.
Well, you still can throw it, but you're not allowed to let it escape the destructor (you need to catch them before they would chain).
This was what I was trying to do: wrap some code in a try-catch in the dtor so that any exceptions thrown won't escape the dtor. However, the current inconsistent behaviour is making this difficult. The same dtor behaves differently depending on whether it was called from a normal end of scope, or while an Exception is in transit. I.e., when called normally, the catch clause catches MyException, but when another Exception is in transit, the catch clause fails to catch MyException (yet it does catch the base class Exception, a rather strange behaviour). T -- The most powerful one-line C program: #include "/dev/tty" -- IOCCC
Aug 21 2017
prev sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 08/19/2017 01:58 PM, Nemanja Boric wrote:

 C++ also provides a way to inspect if you're in the middle of the stack
 unwinding caused by an exception, to make this a bit more controllable,
 and I would think we should provide the similar primitive:
 http://en.cppreference.com/w/cpp/error/uncaught_exception.
I don't know whether C++11 changed matters but according to popular C++98 wisdom, we were told "Don't use [std::uncaught_exception]": www.gotw.ca/gotw/047.htm Ali
Aug 21 2017
parent reply Nemanja Boric <4burgos gmail.com> writes:
On Monday, 21 August 2017 at 20:15:53 UTC, Ali Çehreli wrote:
 On 08/19/2017 01:58 PM, Nemanja Boric wrote:

 C++ also provides a way to inspect if you're in the middle of 
 the stack
 unwinding caused by an exception, to make this a bit more 
 controllable,
 and I would think we should provide the similar primitive:
 .
I don't know whether C++11 changed matters but according to popular C++98 wisdom, we were told "Don't use [std::uncaught_exception]": www.gotw.ca/gotw/047.htm Ali
Rationale for changing this in C++17 is actually comming from Herb Sutter and it is referring to gotw47: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3614.pdf
Aug 22 2017
parent Nemanja Boric <4burgos gmail.com> writes:
On Tuesday, 22 August 2017 at 07:03:03 UTC, Nemanja Boric wrote:
 On Monday, 21 August 2017 at 20:15:53 UTC, Ali Çehreli wrote:
 On 08/19/2017 01:58 PM, Nemanja Boric wrote:

 C++ also provides a way to inspect if you're in the middle of 
 the stack
 unwinding caused by an exception, to make this a bit more 
 controllable,
 and I would think we should provide the similar primitive:
 .
I don't know whether C++11 changed matters but according to popular C++98 wisdom, we were told "Don't use [std::uncaught_exception]": www.gotw.ca/gotw/047.htm Ali
Rationale for changing this in C++17 is actually comming from Herb Sutter and it is referring to gotw47: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3614.pdf
Sorry, on the phone, so I've pasted wrong link: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4152.pdf
Aug 22 2017
prev sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 08/17/2017 02:48 PM, H. S. Teoh via Digitalmars-d wrote:

 So the question becomes, why does the catch block *not*
 catch the instance of MyException when another exception is in transit?!
I caught (!) the same or similar behavior last year: https://issues.dlang.org/show_bug.cgi?id=16177 Ali
Aug 21 2017