View mode: basic / threaded / horizontal-split · Log in · Help
November 28, 2006
scope + destructor with Exception parameter for RAII
What do you think of adding an optional parameter (exception) to the 
destructor, defaulting to null, to indicate the destructor was called 
then unwinding because an exception was thrown? Then you can almost 
forget about scope(exit/success/failure) and you have a RAII as complete 
 as Python's 'with' statement.

So you can do something like:

class Transaction
{
	Database db;
	public:
	this(Database d) { db = d; db.begin(); }
	~this() // success
	{
		db.commit();
	}
	~this(Exception e)
	{
		db.rollback();
	}
	// ...
}

scope Transaction t = new Transaction(db);

This concept could be extended to have as many destructors as you want, 
to handle different kind of exceptions, like:

class Transaction
{
	// ...
	~this() // success
	{
		db.commit();
	}
	~this(DatabaseException e)
	{
		db.rollback();
		// some recovery code
	}
	~this(AnotherException e)
	{
		db.rollback();
		// some other stuff
	}
	~this(Exception e)
	{
		db.rollback();
	}
}

I find it a little ugly and error prone =)
If, for example, you have to use scope(exit/success/failure) you have to 
 add yourself the finalization code on every use:

Each time you want to use a transaction (for example) you have to write:
Transaction t = new Transaction(db);
scope(failure) db.rollback();
scope(exit) db.commit();
// ... code

If you forget one scope(...) db.xxx(); you hit a bug. On the other hand, 
if you could just use:
scope Transaction t = new Transaction(db);

The code is simpler, less error prone, and plus you have all the 
Transaction logic in the transaction object, not in the code that uses 
the transaction.

Opinions? Factibility?

-- 
Leandro Lucarella
Integratech S.A.
4571-5252
November 28, 2006
Re: scope + destructor with Exception parameter for RAII
Leandro Lucarella wrote:
> What do you think of adding an optional parameter (exception) to the 
> destructor, defaulting to null, to indicate the destructor was called 
> then unwinding because an exception was thrown? Then you can almost 
> forget about scope(exit/success/failure) and you have a RAII as complete 
>  as Python's 'with' statement.

I don't like it, personally.  It doesn't seem a good idea for a dtor to 
alter its behavior based on whether an exception is in flight, and 
exceptions should never be thrown from dtors anyway.  Doing so makes 
writing correct code far too complicated.

One thing I have done for Ares, however, is to terminate the program if 
one exception is thrown while another is in flight.  I think DMD/Phobos 
does not do this currently, and instead either ignores the new 
exception, or substitutes it for the in-flight exception (I can't 
remember which).


Sean
November 28, 2006
Re: scope + destructor with Exception parameter for RAII
Sean Kelly wrote:
> Leandro Lucarella wrote:
>> What do you think of adding an optional parameter (exception) to the 
>> destructor, defaulting to null, to indicate the destructor was called 
>> then unwinding because an exception was thrown? Then you can almost 
>> forget about scope(exit/success/failure) and you have a RAII as 
>> complete  as Python's 'with' statement.
> 
> I don't like it, personally.  It doesn't seem a good idea for a dtor to 
> alter its behavior based on whether an exception is in flight, and 
> exceptions should never be thrown from dtors anyway.  Doing so makes 
> writing correct code far too complicated.
> 
> One thing I have done for Ares, however, is to terminate the program if 
> one exception is thrown while another is in flight.  I think DMD/Phobos 
> does not do this currently, and instead either ignores the new 
> exception, or substitutes it for the in-flight exception (I can't 
> remember which).
> 
> 
> Sean


I think this works under DMD

try
{
	throw new Error("some stuff");
}
catch(Error e)
{
	throw new Error("more stuff\n"~e.toString);
}


It could be vary handy to have this ability to swap out one exception 
for another.
November 28, 2006
Re: scope + destructor with Exception parameter for RAII
BCS wrote:
> Sean Kelly wrote:
>> Leandro Lucarella wrote:
>>> What do you think of adding an optional parameter (exception) to the 
>>> destructor, defaulting to null, to indicate the destructor was called 
>>> then unwinding because an exception was thrown? Then you can almost 
>>> forget about scope(exit/success/failure) and you have a RAII as 
>>> complete  as Python's 'with' statement.
>>
>> I don't like it, personally.  It doesn't seem a good idea for a dtor 
>> to alter its behavior based on whether an exception is in flight, and 
>> exceptions should never be thrown from dtors anyway.  Doing so makes 
>> writing correct code far too complicated.
>>
>> One thing I have done for Ares, however, is to terminate the program 
>> if one exception is thrown while another is in flight.  I think 
>> DMD/Phobos does not do this currently, and instead either ignores the 
>> new exception, or substitutes it for the in-flight exception (I can't 
>> remember which).
>>
>>
>> Sean
> 
> 
> I think this works under DMD
> 
> try
> {
>     throw new Error("some stuff");
> }
> catch(Error e)
> {
>     throw new Error("more stuff\n"~e.toString);
> }
> 
> 
> It could be vary handy to have this ability to swap out one exception 
> for another.

Agreed.  Although I don't know if this interferes with Sean's 
interpretation of the spec or not.  I've done this in a few places 
myself, and I find it quite useful.

-- 
- EricAnderton at yahoo
November 28, 2006
Re: scope + destructor with Exception parameter for RAII
Pragma wrote:
> BCS wrote:
>> Sean Kelly wrote:
>>> Leandro Lucarella wrote:
>>>> What do you think of adding an optional parameter (exception) to the 
>>>> destructor, defaulting to null, to indicate the destructor was 
>>>> called then unwinding because an exception was thrown? Then you can 
>>>> almost forget about scope(exit/success/failure) and you have a RAII 
>>>> as complete  as Python's 'with' statement.
>>>
>>> I don't like it, personally.  It doesn't seem a good idea for a dtor 
>>> to alter its behavior based on whether an exception is in flight, and 
>>> exceptions should never be thrown from dtors anyway.  Doing so makes 
>>> writing correct code far too complicated.
>>>
>>> One thing I have done for Ares, however, is to terminate the program 
>>> if one exception is thrown while another is in flight.  I think 
>>> DMD/Phobos does not do this currently, and instead either ignores the 
>>> new exception, or substitutes it for the in-flight exception (I can't 
>>> remember which).
>>>
>>>
>>> Sean
>>
>>
>> I think this works under DMD
>>
>> try
>> {
>>     throw new Error("some stuff");
>> }
>> catch(Error e)
>> {
>>     throw new Error("more stuff\n"~e.toString);
>> }
>>
>>
>> It could be vary handy to have this ability to swap out one exception 
>> for another.
> 
> Agreed.  Although I don't know if this interferes with Sean's 
> interpretation of the spec or not.  I've done this in a few places 
> myself, and I find it quite useful.

The above example isn't the same thing.  The in-flight exception is 
caught and a different one is rethrown.  What I consider illegal is 
something like this:

    try
    {
        throw new Exception( "A" );
    }
    finally
    {
        throw new Exception( "B" );
    }

What exception is in-flight after the finally block completes?  And what 
happens to the other exception?  The only correct behavior here is to 
terminate the application.


Sean
November 28, 2006
Re: scope + destructor with Exception parameter for RAII
Sean Kelly wrote:
> Pragma wrote:
>> BCS wrote:
>>> Sean Kelly wrote:
>>>> Leandro Lucarella wrote:
>>>>> What do you think of adding an optional parameter (exception) to 
>>>>> the destructor, defaulting to null, to indicate the destructor was 
>>>>> called then unwinding because an exception was thrown? Then you can 
>>>>> almost forget about scope(exit/success/failure) and you have a RAII 
>>>>> as complete  as Python's 'with' statement.
>>>>
>>>> I don't like it, personally.  It doesn't seem a good idea for a dtor 
>>>> to alter its behavior based on whether an exception is in flight, 
>>>> and exceptions should never be thrown from dtors anyway.  Doing so 
>>>> makes writing correct code far too complicated.
>>>>
>>>> One thing I have done for Ares, however, is to terminate the program 
>>>> if one exception is thrown while another is in flight.  I think 
>>>> DMD/Phobos does not do this currently, and instead either ignores 
>>>> the new exception, or substitutes it for the in-flight exception (I 
>>>> can't remember which).
>>>>
>>>>
>>>> Sean
>>>
>>>
>>> I think this works under DMD
>>>
>>> try
>>> {
>>>     throw new Error("some stuff");
>>> }
>>> catch(Error e)
>>> {
>>>     throw new Error("more stuff\n"~e.toString);
>>> }
>>>
>>>
>>> It could be vary handy to have this ability to swap out one exception 
>>> for another.
>>
>> Agreed.  Although I don't know if this interferes with Sean's 
>> interpretation of the spec or not.  I've done this in a few places 
>> myself, and I find it quite useful.
> 
> The above example isn't the same thing.  The in-flight exception is 
> caught and a different one is rethrown.  What I consider illegal is 
> something like this:
> 
>     try
>     {
>         throw new Exception( "A" );
>     }
>     finally
>     {
>         throw new Exception( "B" );
>     }

By the way, the change I made will only trap exceptions thrown from an 
object's dtor, as it was a change to the finalizer code.  The above will 
work just fine, with only one of the two exceptions escaping.  Same as:

    scope(exit) throw new Exception( "B" );
    throw new Exception( "A" );

I could probably be convinced that the throw from the finally block 
should simply be ignored, as the finally block is intended to do some 
simple clean-up, but the scope(exit) bit above is clearly a programming 
error.  Throwing from an object's dtor is a lot closer to throwing from 
scope(exit) than finally IMO.  Because while they are functionally 
identical, I think they are likely to be used in different ways.  With a 
finally block, the programmer is stating that he expects an exception to 
be thrown and that the code should execute regardless.  I'm still not 
certain that I like the idea of ignoring an exception from a finally 
block, but it bothers me less than ignoring exceptions from dtors or 
scope(exit)/scope(failure) blocks.


Sean
November 28, 2006
Re: scope + destructor with Exception parameter for RAII
One more follow-up and I promise I'll stop :-)  I just remembered why I 
changed Ares in the first place.  Since objects are finalized during GC 
collections, throwing from a dtor results in completely unpredictable 
program behavior.  For example:

    {
        class MyClass
        {
            ~this()
            {
                throw new Exception( "die" );
            }
        }
        MyClass c = new MyClass();
    }

    OtherClass c = new OtherClass(); // A

In the program above, an exception may be thrown from point A that has 
*nothing to do with an out of memory condition* and worse, it will be 
thrown only if program memory is in a state where the GC needs to 
collect to free up resources.  It's even possible that the instance of 
MyClass could have been declared and allocated in a completely different 
thread, resulting it its being passed up a call stack that was not 
written to expect such a condition.  So the presence of a GC and 
GC-called finalizers makes throwing from dtors even worse than it is in 
deterministic situations (which is still quite bad).

Also, I have found few instances where an exception really needs to be 
thrown from a dtor.  While resource cleanup operations may indeed fail, 
more often than not the documentation will say that a failure may only 
occur if the parameters are invalid.  And with proper encapsulation is 
is guaranteed not to happen.


Sean
November 29, 2006
Re: scope + destructor with Exception parameter for RAII
Sean Kelly escribió:
> Leandro Lucarella wrote:
>> What do you think of adding an optional parameter (exception) to the 
>> destructor, defaulting to null, to indicate the destructor was called 
>> then unwinding because an exception was thrown? Then you can almost 
>> forget about scope(exit/success/failure) and you have a RAII as 
>> complete  as Python's 'with' statement.
> 
> I don't like it, personally.  It doesn't seem a good idea for a dtor to 
> alter its behavior based on whether an exception is in flight, and 
> exceptions should never be thrown from dtors anyway.  Doing so makes 
> writing correct code far too complicated.

Exceptions would not be thrown from the dtor, it just executes some code 
if it was executed during an unwinding because an exception was raised:

scope Transaction t = new Transaction();
throw Exception("ouch!");

Here the Transaction.~this(Exception) is called, but there is no 
Exception throwing in the dtor.

I don't see why writing correct code is that complicated. And how do you 
address the problem of repeating error handling code and the lack of 
encapsulation of scope(success/failure)?

Any other thoughts on this (the thread diverged a little from the 
original topic ;)?

-- 
Leandro Lucarella
Integratech S.A.
4571-5252
November 29, 2006
Re: scope + destructor with Exception parameter for RAII
Leandro Lucarella wrote:
> Sean Kelly escribió:
>> Leandro Lucarella wrote:
>>> What do you think of adding an optional parameter (exception) to the 
>>> destructor, defaulting to null, to indicate the destructor was called 
>>> then unwinding because an exception was thrown? Then you can almost 
>>> forget about scope(exit/success/failure) and you have a RAII as 
>>> complete  as Python's 'with' statement.
>>
>> I don't like it, personally.  It doesn't seem a good idea for a dtor 
>> to alter its behavior based on whether an exception is in flight, and 
>> exceptions should never be thrown from dtors anyway.  Doing so makes 
>> writing correct code far too complicated.
> 
> Exceptions would not be thrown from the dtor, it just executes some code 
> if it was executed during an unwinding because an exception was raised:
> 
> scope Transaction t = new Transaction();
> throw Exception("ouch!");
> 
> Here the Transaction.~this(Exception) is called, but there is no 
> Exception throwing in the dtor.

So are you saying you want this feature to allow Transaction to decide 
whether to roll-back or commit?  I had assumed it was to determine 
whether it was "safe" for Transaction's dtor to throw an exception itself.

> I don't see why writing correct code is that complicated. And how do you 
> address the problem of repeating error handling code and the lack of 
> encapsulation of scope(success/failure)?

The lack of encapsulation doesn't bother me much, though now I see what 
you're getting at.  I do think that having:

    auto scope t = new Transaction();
    scope(failure) t.rollback();
    // commits if not rolled back on scope exit, alternately use
    scope(success) t.commit();

actually aids readability a bit, at the expense of some extra code.

That said, I have considered adding a routine that the user can call to 
determine whether an exception is in flight.  Similar to the one in C++, 
but without all the annoying shortcomings.  It would mean setting a 
thread-local flag or pointer in the internal exception handling code, 
etc.  I think this is a better approach than altering dtor syntax for 
the same purpose, as it avoids language changes and doesn't lose any 
usefulness in the process.

> Any other thoughts on this (the thread diverged a little from the 
> original topic ;)?

See above :-)


Sean
November 30, 2006
Re: scope + destructor with Exception parameter for RAII
Sean Kelly escribió:
>> I don't see why writing correct code is that complicated. And how do 
>> you address the problem of repeating error handling code and the lack 
>> of encapsulation of scope(success/failure)?
> 
> The lack of encapsulation doesn't bother me much, though now I see what 
> you're getting at.  I do think that having:
> 
>     auto scope t = new Transaction();
>     scope(failure) t.rollback();
>     // commits if not rolled back on scope exit, alternately use
>     scope(success) t.commit();
> 
> actually aids readability a bit, at the expense of some extra code.

So you are against all RAII done in the C++ way, I guess...

> That said, I have considered adding a routine that the user can call to 
> determine whether an exception is in flight.  Similar to the one in C++, 
> but without all the annoying shortcomings.  It would mean setting a 
> thread-local flag or pointer in the internal exception handling code, 
> etc.  I think this is a better approach than altering dtor syntax for 
> the same purpose, as it avoids language changes and doesn't lose any 
> usefulness in the process.

I see this more as a hack than a clean solution (I don't say the dtor 
Exception parameter is heaven but I see it a little more cleaner =), but 
its fair enough.

I didn't know C++ had a way to determine an exception is in flight...

>> Any other thoughts on this (the thread diverged a little from the 
>> original topic ;)?
> 
> See above :-)

Thanks, really =)

-- 
Leandro Lucarella
Integratech S.A.
4571-5252
« First   ‹ Prev
1 2
Top | Discussion index | About this forum | D home