www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Proposal: "auto" and exceptions

reply Sean Kelly <sean f4.ca> writes:
Currently, auto classes are only deterministically destroyed if execution leaves
scope normally, ie. if an exception is not thrown (see example below).  To avoid
one potential cause of multiple in-flight exceptions while maintaining the
guarantee of deterministtic destruction, I propose that all auto objects be
cleaned up by the system upon exception recovery--that is, when execution leaves
the scope of a catch block normally rather than via a throw expression.  This
would serve the additional purpose of allowing the programmer to create
non-recoverable errors by throwing an auto object with a terminate statement in
the object's dtor.

Here's an example of current behavior:

# auto class AutoClass
# {
# public:
#     this()
#     {
#         printf( "ctor\n" );
#         throw new Exception( "" );
#     }
#     
#     ~this()
#     {
#         printf( "dtor\n" );
#     }
# }
# 
# 
# void main()
# {
#     try
#     {
#         auto AutoClass c = new AutoClass();
#     }
#     catch( Exception e )
#     {
#         printf( "caught\n" );    
#     }
#     printf( "continue\n" );
# }

Prints:

ctor
caught
continue
dtor

The desired behavior would be to print:

ctor
caught
dtor
continue
Apr 13 2005
next sibling parent reply Carlos <carlos2003nov yahoo.ca> writes:
Sean Kelly wrote:
 The desired behavior would be to print:
 
 ctor
 caught
 dtor
 continue
 
 

Young D Soul wanting to learn speaking: Is this Ok: # auto class AutoClass # { # public: # this() # { try # { printf( "ctor\n" ); # throw new Exception( "" ); # } # catch( Exception e ) # { printf( "caught in ctor, calling dtor\n" ); # delete this; # printf( "dtor called\n" ); # throw new Exception( e.toString() ); # } # } # ~this() # { printf( "dtor\n" ); # } # } # # void main() # { # try # { auto AutoClass c = new AutoClass(); # } # catch( Exception e ) # { printf( "caught in main\n" ); # } # printf( "continue in main\n" ); # } which prints: ctor caught in ctor, calling dtor dtor dtor called caught in main continue in main
Apr 13 2005
parent Sean Kelly <sean f4.ca> writes:
In article <d3jl7f$242d$1 digitaldaemon.com>, Carlos says...
Young D Soul wanting to learn speaking:

Is this Ok:

Yes, though it should not be necessary to do this manually. One slight change below as well.
# auto class AutoClass
# {
# public:
#    this()
#    {  try
#       {  printf( "ctor\n" );
#          throw new Exception( "" );
#       }
#       catch( Exception e )
#       {  printf( "caught in ctor, calling dtor\n" );
#          delete this;
#          printf( "dtor called\n" );

throw e; // just rethrow the old exception
#       }
#    }
#    ~this()
#    {  printf( "dtor\n" );
#    }
# }
#
# void main()
# {
#    try
#    {  auto AutoClass c = new AutoClass();
#    }
#    catch( Exception e )
#    {  printf( "caught in main\n" );
#    }
#    printf( "continue in main\n" );
# }

which prints:

ctor
caught in ctor, calling dtor
dtor
dtor called
caught in main
continue in main

Apr 13 2005
prev sibling parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
"Sean Kelly" <sean f4.ca> wrote in message 
news:d3jhab$20b2$1 digitaldaemon.com...
 Currently, auto classes are only deterministically destroyed if execution 
 leaves
 scope normally, ie. if an exception is not thrown (see example below).

Interesting test. It could be a bug in exception handling and auto classes - it seems odd to run the dtor after both the try and catch scopes are gone. When I run auto class AutoClass { this() { printf( "ctor\n" ); } ~this() { printf( "dtor\n" ); } void yikes() { throw new Exception( "" ); } } void main() { try { auto AutoClass c = new AutoClass(); c.yikes(); } catch( Exception e ){ printf( "caught\n" ); } printf( "continue\n" ); } I get D:\d>test ctor dtor caught continue The only difference is throwing after the ctor vs inside.
Apr 13 2005
next sibling parent reply Sean Kelly <sean f4.ca> writes:
In article <d3jnpd$26hi$1 digitaldaemon.com>, Ben Hinkle says...
"Sean Kelly" <sean f4.ca> wrote in message 
news:d3jhab$20b2$1 digitaldaemon.com...
 Currently, auto classes are only deterministically destroyed if execution 
 leaves
 scope normally, ie. if an exception is not thrown (see example below).

Interesting test. It could be a bug in exception handling and auto classes - it seems odd to run the dtor after both the try and catch scopes are gone. When I run auto class AutoClass { this() { printf( "ctor\n" ); } ~this() { printf( "dtor\n" ); } void yikes() { throw new Exception( "" ); } } void main() { try { auto AutoClass c = new AutoClass(); c.yikes(); } catch( Exception e ){ printf( "caught\n" ); } printf( "continue\n" ); } I get D:\d>test ctor dtor caught continue The only difference is throwing after the ctor vs inside.

Well that's interesting. So the problem is only when an exception is thrown from the ctor? Either way, I'd like to know if anyone has issues with my suggestion, as it seems a simple way to address the Irrecoverable exception idea. Is there any reason RAII would require the auto classes to be cleaned up immediately upon scope exit rather than delaying this until catch scope exit? This practice causes a ton of problems in C++, and it would be nice if these problems could be so neatly eliminated while addressing Matthew's request at the same time. Sean
Apr 13 2005
parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
 Either way, I'd like to know if anyone has issues with my suggestion, as 
 it
 seems a simple way to address the Irrecoverable exception idea.  Is there 
 any
 reason RAII would require the auto classes to be cleaned up immediately 
 upon
 scope exit rather than delaying this until catch scope exit?  This 
 practice
 causes a ton of problems in C++, and it would be nice if these problems 
 could be
 so neatly eliminated while addressing Matthew's request at the same time.

I think the Matthew was proposing that the program exit at the end of the top-most catch block - not the immediate catch block. For example if foo1 calls foo2 calls foo3 and all have try catches then an unrecoverable exception thrown in foo3 will execute all the catches (or finally statements) in foo3, foo2, foo1 in order and then exit. From what I understand of your proposal the prog would exit at the catch with the auto, which I guess the user would have to make sure is the top.
Apr 13 2005
next sibling parent reply Sean Kelly <sean f4.ca> writes:
In article <d3k1cf$2fdg$1 digitaldaemon.com>, Ben Hinkle says...
 Either way, I'd like to know if anyone has issues with my suggestion, as 
 it
 seems a simple way to address the Irrecoverable exception idea.  Is there 
 any
 reason RAII would require the auto classes to be cleaned up immediately 
 upon
 scope exit rather than delaying this until catch scope exit?  This 
 practice
 causes a ton of problems in C++, and it would be nice if these problems 
 could be
 so neatly eliminated while addressing Matthew's request at the same time.

I think the Matthew was proposing that the program exit at the end of the top-most catch block - not the immediate catch block. For example if foo1 calls foo2 calls foo3 and all have try catches then an unrecoverable exception thrown in foo3 will execute all the catches (or finally statements) in foo3, foo2, foo1 in order and then exit. From what I understand of your proposal the prog would exit at the catch with the auto, which I guess the user would have to make sure is the top.

No. I was proposing that auto class destruction be deferred until a catch clause exits normally rather than with another throw exception. By Matthew's terminology, that would be the top-most catch block. ie. # void f1() { # try { # auto Foo f = new Foo(); # throw new Exception(""); # catch(Exception e) { # throw e; # } # } # # void f2() { # try { # auto Bar b = new Bar(); # f1(); # } # catch(Exception e) { # throw new Exception(""); # } # } # # void f3() { # try { # f2(); # } # catch(Exception e) {} # // auto Foo f would be destroyed here, followed by auto Bar b # } The only slightly sticky issue I can think of is how do auto objects declared within one of the catch blocks fit into the destruction ordering scheme? Should destruction be strictly LIFO or should a more complex ordering be imposed? Sean
Apr 13 2005
parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
 No.  I was proposing that auto class destruction be deferred until a catch
 clause exits normally rather than with another throw exception.  By 
 Matthew's
 terminology, that would be the top-most catch block.  ie.

Plus Matthew would require all catch blocks to rethrow automatically until the top-most block. Otherwise with the foo1, foo2, foo3 example if foo2 didn't rethrow then it would leave the catch block normally and foo1's catch wouldn't run. I understand Matthew's request as: if an instance of a certain class of objects is thrown then all catch blocks on the stack automatically rethrow if the catch block is exited normally (ie - no catch block can be exited normally).
Apr 13 2005
parent reply Sean Kelly <sean f4.ca> writes:
In article <d3k73p$2k73$1 digitaldaemon.com>, Ben Hinkle says...
 No.  I was proposing that auto class destruction be deferred until a catch
 clause exits normally rather than with another throw exception.  By 
 Matthew's
 terminology, that would be the top-most catch block.  ie.

Plus Matthew would require all catch blocks to rethrow automatically until the top-most block. Otherwise with the foo1, foo2, foo3 example if foo2 didn't rethrow then it would leave the catch block normally and foo1's catch wouldn't run. I understand Matthew's request as: if an instance of a certain class of objects is thrown then all catch blocks on the stack automatically rethrow if the catch block is exited normally (ie - no catch block can be exited normally).

But the point of forcing a rethrow all the way out of main would just be to insure termination, correct? If so, this would be handled by terminate() in the class dtor in my suggestion. My only (growing) reservation is that I'm not entirely happy with the impact on auto objects in evaluated catch blocks, as I think they should be destroyed *after* the in-function auto objects whose destruction was deferred. ie. I would want destruction to be delayed, but destruction order to be preserved, and this isn't strictly LIFO, which would slightly complicate implementation (and explanation). Sean
Apr 13 2005
parent reply "Matthew" <admin stlsoft.dot.dot.dot.dot.org> writes:
"Sean Kelly" <sean f4.ca> wrote in message 
news:d3k88o$2l35$1 digitaldaemon.com...
 In article <d3k73p$2k73$1 digitaldaemon.com>, Ben Hinkle says...
 No.  I was proposing that auto class destruction be deferred 
 until a catch
 clause exits normally rather than with another throw exception. 
 By
 Matthew's
 terminology, that would be the top-most catch block.  ie.

Plus Matthew would require all catch blocks to rethrow automatically until the top-most block. Otherwise with the foo1, foo2, foo3 example if foo2 didn't rethrow then it would leave the catch block normally and foo1's catch wouldn't run. I understand Matthew's request as: if an instance of a certain class of objects is thrown then all catch blocks on the stack automatically rethrow if the catch block is exited normally (ie - no catch block can be exited normally).

But the point of forcing a rethrow all the way out of main would just be to insure termination, correct? If so, this would be handled by terminate() in the class dtor in my suggestion. My only (growing) reservation is that I'm not entirely happy with the impact on auto objects in evaluated catch blocks, as I think they should be destroyed *after* the in-function auto objects whose destruction was deferred. ie. I would want destruction to be delayed, but destruction order to be preserved, and this isn't strictly LIFO, which would slightly complicate implementation (and explanation).

It's also because different levels of the call chain should be given a bite at the last-gasps-of-life in an irrecoverable situation. Otherwise, how does one decide, from a program behavioural policy point of view, who should get it?
Apr 13 2005
parent Sean Kelly <sean f4.ca> writes:
In article <d3kpul$30kk$2 digitaldaemon.com>, Matthew says...
It's also because different levels of the call chain should be given 
a bite at the last-gasps-of-life in an irrecoverable situation. 
Otherwise, how does one decide, from a program behavioural policy 
point of view, who should get it?

To me, an Error is just like any other exception but for the fact that it is not recoverable. And so long as errors are not grouped under the Exception umbrella, the client will have to catch them explicitly anyway. I have nothing against re-throwing all the way up the call chain--it just seems unnecessary so long as the application terminates when the client is done with it. How far that Error gets, IMO, is entirely up to the client, just so long as the choice is made explicitly. Sean
Apr 13 2005
prev sibling parent "Matthew" <admin stlsoft.dot.dot.dot.dot.org> writes:
"Ben Hinkle" <bhinkle mathworks.com> wrote in message 
news:d3k1cf$2fdg$1 digitaldaemon.com...
 Either way, I'd like to know if anyone has issues with my 
 suggestion, as it
 seems a simple way to address the Irrecoverable exception idea. 
 Is there any
 reason RAII would require the auto classes to be cleaned up 
 immediately upon
 scope exit rather than delaying this until catch scope exit? 
 This practice
 causes a ton of problems in C++, and it would be nice if these 
 problems could be
 so neatly eliminated while addressing Matthew's request at the 
 same time.

I think the Matthew was proposing that the program exit at the end of the top-most catch block - not the immediate catch block. For example if foo1 calls foo2 calls foo3 and all have try catches then an unrecoverable exception thrown in foo3 will execute all the catches (or finally statements) in foo3, foo2, foo1 in order and then exit. From what I understand of your proposal the prog would exit at the catch with the auto, which I guess the user would have to make sure is the top.

Ben describes it aright
Apr 13 2005
prev sibling parent reply "Walter" <newshound digitalmars.com> writes:
"Ben Hinkle" <bhinkle mathworks.com> wrote in message
news:d3jnpd$26hi$1 digitaldaemon.com...
 The only difference is throwing after the ctor vs inside.

That's right. The issue here is what happens when an exception is thrown within a constructor. It is not an issue of autos being cleaned up if an exception gets thrown - they are.
Apr 15 2005
parent Sean Kelly <sean f4.ca> writes:
In article <d3q6i9$1th4$2 digitaldaemon.com>, Walter says...
"Ben Hinkle" <bhinkle mathworks.com> wrote in message
news:d3jnpd$26hi$1 digitaldaemon.com...
 The only difference is throwing after the ctor vs inside.

That's right. The issue here is what happens when an exception is thrown within a constructor. It is not an issue of autos being cleaned up if an exception gets thrown - they are.

Yup. My mistake for grabbing my example from a collection of bug tests on exceptions... and picking one that demonstrated a different problem. The only bug in the code I posted is that the class dtor is called at all. Sean
Apr 17 2005