Topic: Proposal: exception stack unwinding resumption


Author: news_comp.std.c++_expires-2002-10-01@nmhq.net (Niklas Matthies)
Date: Tue, 8 Oct 2002 17:10:42 +0000 (UTC)
Raw View
On Tue, 24 Sep 2002 12:23:14 +0000 (UTC), Bob Bell <belvis@pacbell.net> wrote:
>  news_comp.std.c++_expires-2002-10-01@nmhq.net (Niklas Matthies) wrote in message news:<slrnaouku5.43d.news_comp.std.c++_expires-2002-10-01@moongate.nmhq.net>...
> > The only reason why never-throwing destructors are considered best
> > practice today in C++ is that its exception handling mechanism wasn't
> > designed to deal with more than one error at a time. I believe it's
> > very reasonable to think about how this situation could be improved, so
> > that less (or ideally no) information about errors gets lost.
>
>  So how do you propose that the language should handle multiple
>  exceptions at once?

I don't, currently. While I do have a number of ideas of how such
handling could or should look like, it doesn't make much sense to start
discussing possible approaches as long as there is no agreement that
there is an actual problem in need of solving.

Since that question presently isn't really standard-specific any more,
please see my posting [1] in the thread "Exceptions in Destructors" over
on comp.lang.c++.moderated.

[1] <slrnaq48m0.14k.news_comp.lang.c++.moderated_expires-2002-10-01@nightrunner.nmhq.net>
    news:slrnaq48m0.14k.news_comp.lang.c++.moderated_expires-2002-10-01@nightrunner.nmhq.net
    http://groups.google.com/groups?selm=slrnaq48m0.14k.news_comp.lang.c%2B%2B.moderated_expires-2002-10-01%40nightrunner.nmhq.net
    (article hadn't shown up on Google Groups yet at the time of writing)

-- Niklas Matthies
--
Save Farscape - get informed and involved: http://farscape.wdsection.com/
Together, we can get Farscape back on air. Crackers *do* matter.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: pierrebai@hotmail.com (Pierre Baillargeon)
Date: Fri, 27 Sep 2002 11:50:47 +0000 (UTC)
Raw View
francis.glassborow@ntlworld.com (Francis Glassborow) wrote in message news:<ZRjjgCB9B1k9Ewft@robinton.demon.co.uk>...
>
> I maintain that any dtor that _needs_ to throw is perverting the
> semantics of destructors.  If it does not need to throw then it should
> not do so under any circumstances.

I respect your opinion and design choices, but is this a valid reason
to actively resists having the C++ language support those that would
like to write such code?

Many things about C++ bother many people, yet the language doesn't
force anything down anyone's throat. My proposal doesn't force you to
use destructors that throw, and its formulation should not incur any
cost if your destructor don't throw. So what is the harm?

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: glancaster@ntlworld.com ("Garry Lancaster")
Date: Fri, 27 Sep 2002 11:53:54 +0000 (UTC)
Raw View
Pierre Baillageon:
> >> I'd like to be corrected if I'm wrong, but I'm quite certain that if
> >> ~A() throws, all its data members and base classes are destructed. The
> >> relevant clause, I believe, is 15.2, where it is said that partially
> >> constructed objects will have their fully constructed parts destroyed
> >> properly. It even mention that it works for arrays.

Bob Bell:
> >I don't have a copy of the standard, but I'll give you my
> >understanding of the language. Perhaps someone else who does have the
> >standard can settle this for us.
> >
> >"Partially constructed objects" refers to exceptions that are thrown
> >by a constructor, not a destructor. The intent is for the language to
> >guarantee that objects the programmer cannot access are destroyed
> >(since the object has not yet finished constructing, the programmer
> >has not been able to obtain a reference to it).
> >
> >If an exception is thrown from some function that is _not_ a
> >constructor, then only stack objects are destroyed. Therefore, if
> >A::~A above throws, since the B part is not a stack object, B::~B will
> >not be called.
> >
> >I'd be interested in anybody else's opinion on this as well. I just
> >ran a quick test in CodeWarrior 7, and it seems to agree with my
> >interpretation.

Francis Glassborow:
> I am not sure that the Standard actually deals with this. There quite a
> number of corner problems with dtors which matter not at all until
> people want to push the boundaries. But it matters not, because
> supposing that the base subobject dtors are called all they do is make
> matters worse by ensuring that you have a ghost object with some of its
> limbs missing.
>
> However I have just looked up the requisite part of the C++ Standard.
> 15.2 para 2 starts:
>
> An object that is partially constructed or partially destroyed will have
> destructors executed for all of its fully constructed subobjects, that
> is, for subobjects for which the constructor has completed execution and
> the destructor has not yet begun execution.
>
> That makes it quite clear either CodeWarrior got it wrong or your test
> is faulty.

There are two more possibilities:

1. We misinterpret the standard.
2. The standard has a defect.

Let's say we have something like this:

#include <iostream>
#include <string>
#include <exception>

class B
{
public:
 ~B()
    {
     std::cout << "~B\n";
        if( std::uncaught_exception() ) std::cout << "Unwinding.\n";
        throw std::string( "~B" );
    }
};

class A : public B
{
public:
 ~A()


  std::cout << "~A\n";
  throw std::string( "~A" );
 }
};

int main()
{
 try
 {
  A a;
    }
    catch(const std::string s)
    {
     std::cout << "Caught exception thrown by " << s << ".\n";
    }
    std::cout << "Exiting normally.\n";
}

If I understand Bob Bell's reasoning correctly, he is saying that
the program should print:

~A
Caught exception thrown by ~A.
Exiting normally.

I wonder if CodeWarrior 7 would agree?

MSVC7 gives:

~A
~B
Unwinding.

then terminates.

and Borland C++ Builder 5 gives:

~A
~B

then terminates.

Make of this what you will.

Kind regards

Garry Lancaster
Codemill Ltd
Visit our web site at http://www.codemill.net

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: pierrebai@hotmail.com (Pierre Baillargeon)
Date: Fri, 27 Sep 2002 15:31:11 +0000 (UTC)
Raw View
belvis@pacbell.net (Bob Bell) wrote in message news:<c87c1cfb.0209252107.70dc6f8e@posting.google.com>...
> pierrebai@hotmail.com (Pierre Baillargeon) wrote in message news:<6df0c6a8.0209240716.6fb5ec00@posting.google.com>...
>
> Your proposal doesn't make it safe to throw from a destructor. It just
> prevents the program from calling std::terminate when a destructor
> throws, which is somewhat different. I guess that's the capsule
> summary of what I don't like about this proposal.

[...]

> I can't speak for Francis, but I would interpret his remark as
> "destructors should behave as if they were declared with a throw()
> exception specification." In other words, any exception escaping a
> destructor would lead to a call to std::unexpected. That's the
> behavior I would like.

[...]

> If an exception is thrown from some function that is _not_ a
> constructor, then only stack objects are destroyed. Therefore, if
> A::~A above throws, since the B part is not a stack object, B::~B will
> not be called.
>
> I'd be interested in anybody else's opinion on this as well. I just
> ran a quick test in CodeWarrior 7, and it seems to agree with my
> interpretation.

[...]

> > I agree, it's the weak point of my proposal. Would it be more
> > acceptable if the proposed behavior would always be enabled? That
> > would not change the meaning of existing code, since it has really
> > been properly tested and reviewed right? ;-). Seriously folks, would
> > the proposal be more acceptable like that? I didn't say acceptable
> > (yet), just more...
>
> Actually, it would be a step in the right direction. But getting back
> to the capsule summary line at the beginning of this post, it still
> would not make exceptions exiting from destructors safe.

Now I think I understand the fundamental misunderstanding we had: I
assumed that throwing from a destructor destroyed the data members and
base classes, while you didn't.

I rechecked the standard (after reading Francis Glassborow's reply):
it does mention partially destructed objects, so I was right and
CodeWarrior 7 is wrong.

So the capsule summary of my proposal is rather: if a destructor
throws an exception, and if terminate() would be called, it is as if a
try block catched the exception.

I still find it amazing that people have objections to this. I
understand that people could refuse to design destructors that can
throw, but I find it strange that they would actively resists having
the C++ language support those that would like to write such code.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: belvis@pacbell.net (Bob Bell)
Date: Fri, 27 Sep 2002 11:35:36 CST
Raw View
francis.glassborow@ntlworld.com (Francis Glassborow) wrote in message news:<ZRjjgCB9B1k9Ewft@robinton.demon.co.uk>...
> In article <c87c1cfb.0209252107.70dc6f8e@posting.google.com>, Bob Bell
> <belvis@pacbell.net> writes
> >> I'd like to be corrected if I'm wrong, but I'm quite certain that if
> >> ~A() throws, all its data members and base classes are destructed. The
> >> relevant clause, I believe, is 15.2, where it is said that partially
> >> constructed objects will have their fully constructed parts destroyed
> >> properly. It even mention that it works for arrays.
> >
> >I don't have a copy of the standard, but I'll give you my
> >understanding of the language. Perhaps someone else who does have the
> >standard can settle this for us.
> >
> >"Partially constructed objects" refers to exceptions that are thrown
> >by a constructor, not a destructor. The intent is for the language to
> >guarantee that objects the programmer cannot access are destroyed
> >(since the object has not yet finished constructing, the programmer
> >has not been able to obtain a reference to it).
> >
> >If an exception is thrown from some function that is _not_ a
> >constructor, then only stack objects are destroyed. Therefore, if
> >A::~A above throws, since the B part is not a stack object, B::~B will
> >not be called.
> >
> >I'd be interested in anybody else's opinion on this as well. I just
> >ran a quick test in CodeWarrior 7, and it seems to agree with my
> >interpretation.
>
>
> I am not sure that the Standard actually deals with this. There quite a
> number of corner problems with dtors which matter not at all until
> people want to push the boundaries. But it matters not, because
> supposing that the base subobject dtors are called all they do is make
> matters worse by ensuring that you have a ghost object with some of its
> limbs missing.
>
> However I have just looked up the requisite part of the C++ Standard.
> 15.2 para 2 starts:
>
> An object that is partially constructed or partially destroyed will have
> destructors executed for all of its fully constructed subobjects, that
> is, for subobjects for which the constructor has completed execution and
> the destructor has not yet begun execution.
>
> That makes it quite clear either CodeWarrior got it wrong or your test
> is faulty. Note that the above requirement means that you could not even
> resurrect the object as most of it is gone. I suppose that you could
> place any pointers to any unreleased resources into container somewhere
> for latter handling. But you could do that anyway without propagating an
> exception out of a dtor.

Well, I'm convinced. Looks like I was wrong on this.

> I maintain that any dtor that _needs_ to throw is perverting the
> semantics of destructors.  If it does not need to throw then it should
> not do so under any circumstances.

I agree 100%.

Bob

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: francis.glassborow@ntlworld.com (Francis Glassborow)
Date: Fri, 27 Sep 2002 16:42:55 +0000 (UTC)
Raw View
In article <gGVk9.2334$sh4.110044@newsfep2-win.server.ntli.net>, Garry
Lancaster <glancaster@ntlworld.com> writes
>#include <iostream>
>#include <string>
>#include <exception>
>
>class B
>{
>public:
> ~B()
>    {
>     std::cout << "~B\n";
>        if( std::uncaught_exception() ) std::cout << "Unwinding.\n";
>        throw std::string( "~B" );
>    }
>};
>
>class A : public B
>{
>public:
> ~A()
>
>
>  std::cout << "~A\n";
>  throw std::string( "~A" );
> }
>};
>
>int main()
>{
> try
> {
>  A a;
>    }
>    catch(const std::string s)
>    {
>     std::cout << "Caught exception thrown by " << s << ".\n";
>    }
>    std::cout << "Exiting normally.\n";
>}
>
>If I understand Bob Bell's reasoning correctly, he is saying that
>the program should print:
>
>~A
>Caught exception thrown by ~A.
>Exiting normally.
>
>I wonder if CodeWarrior 7 would agree?
>
>MSVC7 gives:
>
>~A
>~B
>Unwinding.
>
>then terminates.
>
>and Borland C++ Builder 5 gives:
>
>~A
>~B
>
>then terminates.

And if I have followed that code correctly (allowing for typos)

I believe that it should produce:

~A
~B
Unwinding
Caught exception thrown by ~A
exiting normally

And I think that is the only meaningful interpretation of the sentences
I quoted.


--
Francis Glassborow      ACCU
64 Southfield Rd
Oxford OX4 1PA          +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: francis.glassborow@ntlworld.com (Francis Glassborow)
Date: Sat, 28 Sep 2002 04:36:22 +0000 (UTC)
Raw View
In article <6df0c6a8.0209261901.36511fd8@posting.google.com>, Pierre
Baillargeon <pierrebai@hotmail.com> writes
>francis.glassborow@ntlworld.com (Francis Glassborow) wrote in message news:<ZRjjgCB9B1k9Ewft@robinton.demon.co.uk>...
>>
>> I maintain that any dtor that _needs_ to throw is perverting the
>> semantics of destructors.  If it does not need to throw then it should
>> not do so under any circumstances.
>
>I respect your opinion and design choices, but is this a valid reason
>to actively resists having the C++ language support those that would
>like to write such code?
>
>Many things about C++ bother many people, yet the language doesn't
>force anything down anyone's throat. My proposal doesn't force you to
>use destructors that throw, and its formulation should not incur any
>cost if your destructor don't throw. So what is the harm?

There is a big difference between allowing people to shoot themselves in
the foot and going to the trouble of constructing a new weapon whose
only purpose is foot shooting :-)

The harm is that pursuing this will cost many people a lot of time, that
means that there needs to be real benefits.


--
Francis Glassborow      ACCU
64 Southfield Rd
Oxford OX4 1PA          +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: francis.glassborow@ntlworld.com (Francis Glassborow)
Date: Sat, 28 Sep 2002 04:36:29 +0000 (UTC)
Raw View
In article <6df0c6a8.0209261854.4b087f03@posting.google.com>, Pierre
Baillargeon <pierrebai@hotmail.com> writes
>I still find it amazing that people have objections to this. I
>understand that people could refuse to design destructors that can
>throw, but I find it strange that they would actively resists having
>the C++ language support those that would like to write such code.

The problem is cost-benefit. Providing such support would be very
expensive both in the human resources used by WG21 and in the
development costs for compiler vendors. The proponents need to persuade
us that it is worth those costs.

--
Francis Glassborow      ACCU
64 Southfield Rd
Oxford OX4 1PA          +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: belvis@pacbell.net (Bob Bell)
Date: Sat, 28 Sep 2002 12:02:47 +0000 (UTC)
Raw View
pierrebai@hotmail.com (Pierre Baillargeon) wrote in message news:<6df0c6a8.0209261901.36511fd8@posting.google.com>...
> francis.glassborow@ntlworld.com (Francis Glassborow) wrote in message news:<ZRjjgCB9B1k9Ewft@robinton.demon.co.uk>...
> >
> > I maintain that any dtor that _needs_ to throw is perverting the
> > semantics of destructors.  If it does not need to throw then it should
> > not do so under any circumstances.
>
> I respect your opinion and design choices, but is this a valid reason
> to actively resists having the C++ language support those that would
> like to write such code?
>
> Many things about C++ bother many people, yet the language doesn't
> force anything down anyone's throat. My proposal doesn't force you to
> use destructors that throw, and its formulation should not incur any
> cost if your destructor don't throw. So what is the harm?

It allows you to subvert the guarantees about exception propagation.

It also allows you to write code that subverts the guarantee that
objects are properly destroyed. C++ bends over backwards to make this
guarantee; allowing language constructs to get around it introduces a
hole in the language, makes the language less consistent, creates the
possibility of subtle bugs, and adds no benefit.

The last point is worth elaborating: if destructors can silently
throw, what have you gained? Is your program more correct? Is it
easier to make correct? No. If a destructor throws and you ignore it,
all that's happened is you've ignored an error. This situation is
similar to ignoring error result codes, which is one of the problems
exceptions were invented to solve.

Bob

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: pierrebai@hotmail.com (Pierre Baillargeon)
Date: Sat, 28 Sep 2002 18:35:03 +0000 (UTC)
Raw View
francis.glassborow@ntlworld.com (Francis Glassborow) wrote in message news:<L7IHaceUkIl9Ewmv@robinton.demon.co.uk>...
>
> The problem is cost-benefit. Providing such support would be very
> expensive both in the human resources used by WG21 and in the
> development costs for compiler vendors. The proponents need to persuade
> us that it is worth those costs.

This is exactly the kind of response I hoped for. Here are the steps I
intend to follow now:

- Modify GCC 3.2 to support my proposal. This will provide an idea of
the cost for vendors.

- Modify the C++ standard text. This will provide an estimate of the
cost for the WG21 and other involved in standardization.

- Gather the costs and benefits for users. I will keep all relevant
information from this thread.

I expect all this to take me a while, but if anyone has specific costs
or benefits that should be included, please reply to this post.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: andys@evo6.com (Andy Sawyer)
Date: Sat, 28 Sep 2002 21:47:22 +0000 (UTC)
Raw View
glancaster@ntlworld.com ("Garry Lancaster") writes:

> MSVC7 gives:
>
> ~A
> ~B
> Unwinding.
>
> then terminates.

Como C++ gives:

,----
| ~A
| ~B
| Unwinding.
| C++ runtime abort: terminate() called by the exception handling
| mechanism
|
| abnormal program termination
`----

Which is what I expect.

--
"Light thinks it travels faster than anything but it is wrong. No matter
 how fast light travels it finds the darkness has always got there first,
 and is waiting for it."                  -- Terry Pratchett, Reaper Man

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: pierrebai@hotmail.com (Pierre Baillargeon)
Date: Mon, 30 Sep 2002 15:34:13 +0000 (UTC)
Raw View
belvis@pacbell.net (Bob Bell) wrote in message news:<c87c1cfb.0209271115.5145cfe6@posting.google.com>...
> pierrebai@hotmail.com (Pierre Baillargeon) wrote in message news:<6df0c6a8.0209261901.36511fd8@posting.google.com>...
> > francis.glassborow@ntlworld.com (Francis Glassborow) wrote in message news:<ZRjjgCB9B1k9Ewft@robinton.demon.co.uk>...
> > >
> > > I maintain that any dtor that _needs_ to throw is perverting the
> > > semantics of destructors.  If it does not need to throw then it should
> > > not do so under any circumstances.
> >
> > I respect your opinion and design choices, but is this a valid reason
> > to actively resists having the C++ language support those that would
> > like to write such code?
> >
> > Many things about C++ bother many people, yet the language doesn't
> > force anything down anyone's throat. My proposal doesn't force you to
> > use destructors that throw, and its formulation should not incur any
> > cost if your destructor don't throw. So what is the harm?
>
> It allows you to subvert the guarantees about exception propagation.

Am I allowed to say "puleaze"? First Francis accuse me of perverting
C++, now subversion? Do I have the right of being tired of the
vocabulary employed to characterize my proposition?

Yes, in an extremely rare cases, which currently would cause
terminate() to be called, and for which the current best practice is
to swallow the exception in a try block, the exception propagation
would be stopped. No one is forcing this one anyone. If you don't
throw in your destructors, my proposal won't change a thing. So
everyone, stop the FUD and the dirty words.

> It also allows you to write code that subverts the guarantee that
> objects are properly destroyed. C++ bends over backwards to make this
> guarantee; allowing language constructs to get around it introduces a
> hole in the language, makes the language less consistent, creates the
> possibility of subtle bugs, and adds no benefit.

This is patently false. My proposal doesn't change a thing about
objects being destroyed correctly. I don't see how it makes things
inconsistent. If it changes anything then your class destructor was
buggy to start with. And I failed to see why my needs, the
possibilities and current uses I've talked about, and those of others
who agreed with my proposal, constitute no benefit.

> The last point is worth elaborating: if destructors can silently
> throw, what have you gained? Is your program more correct? Is it
> easier to make correct? No.

I'm sorry, but the answer is yes. See below.

> If a destructor throws and you ignore it,
> all that's happened is you've ignored an error. This situation is
> similar to ignoring error result codes, which is one of the problems
> exceptions were invented to solve.

When you are dealing with an error and must do clean-up, you have
multiple choices, which can all be used concurrently in different part
of your program depending on needs:

- Ignore subsequent errors: the goal of my proposal is too ease this.
- Replace the first error by the second: supported by try/catch/throw
2nd.
- Treat all in parallel: supported by call-backs.
- Die: supported by terminate().

Only the fourth would go away with my proposal. this is the loss you
mourn.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: belvis@pacbell.net (Bob Bell)
Date: Mon, 30 Sep 2002 16:48:09 +0000 (UTC)
Raw View
glancaster@ntlworld.com ("Garry Lancaster") wrote in message news:<gGVk9.2334$sh4.110044@newsfep2-win.server.ntli.net>...
> Francis Glassborow:
> > I am not sure that the Standard actually deals with this. There quite a
> > number of corner problems with dtors which matter not at all until
> > people want to push the boundaries. But it matters not, because
> > supposing that the base subobject dtors are called all they do is make
> > matters worse by ensuring that you have a ghost object with some of its
> > limbs missing.
> >
> > However I have just looked up the requisite part of the C++ Standard.
> > 15.2 para 2 starts:
> >
> > An object that is partially constructed or partially destroyed will have
> > destructors executed for all of its fully constructed subobjects, that
> > is, for subobjects for which the constructor has completed execution and
> > the destructor has not yet begun execution.
> >
> > That makes it quite clear either CodeWarrior got it wrong or your test
> > is faulty.
>
> There are two more possibilities:
>
> 1. We misinterpret the standard.
> 2. The standard has a defect.
>
> Let's say we have something like this:
>
> #include <iostream>
> #include <string>
> #include <exception>
>
> class B
> {
> public:
>  ~B()
>     {
>      std::cout << "~B\n";
>         if( std::uncaught_exception() ) std::cout << "Unwinding.\n";
>         throw std::string( "~B" );
>     }
> };
>
> class A : public B
> {
> public:
>  ~A()
>
>
>   std::cout << "~A\n";
>   throw std::string( "~A" );
>  }
> };
>
> int main()
> {
>  try
>  {
>   A a;
>     }
>     catch(const std::string s)
>     {
>      std::cout << "Caught exception thrown by " << s << ".\n";
>     }
>  std::cout << "Exiting normally.\n";
> }
>
> If I understand Bob Bell's reasoning correctly, he is saying that
> the program should print:
>
> ~A
> Caught exception thrown by ~A.
> Exiting normally.
>
> I wonder if CodeWarrior 7 would agree?

CodeWarrior did indeed print:

~A
Caught exception thrown by ~A.
Exiting normally.

I hadn't considered the possiblility of a defect. Although it seems
like Pierre's and Francis' interpretation seems to make more sense
than mine. In any case, it doesn't really seem to matter, because
either interpretation leads to incompletely destroyed objects, which
reinforces the idea that destructors should never throw.

Bob

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: pierrebai@hotmail.com (Pierre Baillargeon)
Date: Thu, 26 Sep 2002 16:44:37 +0000 (UTC)
Raw View
francis.glassborow@ntlworld.com (Francis Glassborow) wrote in message news:<IayXyeC7nek9Ewt0@robinton.demon.co.uk>...
> In article <6df0c6a8.0209240716.6fb5ec00@posting.google.com>, Pierre
> Baillargeon <pierrebai@hotmail.com> writes
> >OK, then I'll admit that it is the preferred method. But there is a
> >nagging detail: what I would put in the destructor, the footer(), you
> >have to put somewhere else, right? That somewhere else, in the face of
> >an exception, must catch all exceptions, right? So what is different
> >here? Only the location of the code, not the behavior. Let's take a
> >real life example:
>
> The fundamental difference is that anywhere else leaves you with a
> 'live' object. Once a dtor starts running the object is dead, it is
> gone, unless you are going to propose resurrecting ctors which, IMHO,
> would be a complete mess.

The expected reply. The only time I have ever seen a retry strategy is
for I/O where the read() and write() are specified to possibly read or
write less than requested. But this can hardly be called a failure,
just a type producer/consumer relationship, where two processes can't
be expected to work at the same rate.

Do you really have objects that throw exceptions which are catched and
the operation retried? In 10 years of C++ programming, I have never
written (nor seen) something like that. But in the few years I've read
comp.lang.c++ and comp.std.c++, I've seen this puppet far too often in
reply to exceptions in destructors.

Frankly, what percentage of methods that fail are ever retried in your
programming experience? Is it so pervasive that it's a valid rebuttal
to doing work in destructors? After all, I don't advocate to put all
code in the destructors either.

--
Pierre B.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: belvis@pacbell.net (Bob Bell)
Date: Thu, 26 Sep 2002 16:45:12 +0000 (UTC)
Raw View
pierrebai@hotmail.com (Pierre Baillargeon) wrote in message news:<6df0c6a8.0209240716.6fb5ec00@posting.google.com>...
> belvis@pacbell.net (Bob Bell) wrote in message news:<c87c1cfb.0209231924.6b639ab2@posting.google.com>...
> > pierrebai@hotmail.com (Pierre Baillargeon) wrote in message news:<6df0c6a8.0209230602.2aba3ca8@posting.google.com>...
> > > belvis@pacbell.net (Bob Bell) wrote in message news:<c87c1cfb.0209220939.46d823af@posting.google.com>...
> > > > pierrebai@hotmail.com (Pierre Baillargeon) wrote in message news:<6df0c6a8.0209201925.6faa9e57@posting.google.com>...
> > >
> >
> > I disagree here. Current best practice seems to do pretty well
> > already. My point is that you haven't yet established (to my
> > satisfaction anyway) that current best practice is insufficient.
>
> The current best practice (CBP) is sufficient for the current
> situation where the language makes it difficult to determine if it is
> safe to throw from the destructor. But anyway, I am very interested
> about what would be a convincing argument for you and the other
> detractors.

Your proposal doesn't make it safe to throw from a destructor. It just
prevents the program from calling std::terminate when a destructor
throws, which is somewhat different. I guess that's the capsule
summary of what I don't like about this proposal.

> I agree that the main weakness of the proposal is the switchable
> behavior. I only proposed the switchable behavior to make the proposal
> more acceptable to people who like the current terminate() calls.
>
> I'm starting to believe that I was wrong on that point. Most
> detractors mentionned that point. What's more, I've just read Francis
> Glassborow's post in which he mention that he would like:
>
>    "> destructors never throw.
>       [snipped]
>     And I wish we could actually make that the default for dtors"
>
> Which I take it to mean that they would have an automatic invisible
> try/catch block. My proposal only difference is to allow the exception
> to escape the destructors when it could.

I can't speak for Francis, but I would interpret his remark as
"destructors should behave as if they were declared with a throw()
exception specification." In other words, any exception escaping a
destructor would lead to a call to std::unexpected. That's the
behavior I would like.

> > How do I test that a destructor will never throw? One simple way,
> > mentioned by you, is to wrap every destructor in try/catch. Another
> > way, mentioned by Francis in his reply, is don't call functions that
> > can throw. Usually, an inspection or review of the destructor code is
> > all that is needed to verify that the destructor does not call
> > throwing functions. This is viable because most destructors (that I
> > write anyway) are trivial compared to the rest of the program, and
> > usually do little more than release resources.
>
> It's all true, but a bit of a simplification, IMO. In code reviews,
> nothing in the call signature of a function tells you that it will
> throw or not. It's always a matter of memory and consistent
> maintenance to know that. Of course if you don't want your destructors
> to throw you will keep them doing the least possible work. I know that
> convincing people that expanding their usefulness is a hard battle.

Destructors really shouldn't do much work. They definately shouldn't
do things that can fail.

> > I agree with him that classes that can throw from destructors are
> > broken. Consider a class A with a base class B. A::~A runs and throws.
> > Not only is A::~A interrupted (which may cause it to leak resources,
> > etc.), but the B::~B never gets called!
> >
> > Note that your proposal doesn't make this situation better.
>
> I'd like to be corrected if I'm wrong, but I'm quite certain that if
> ~A() throws, all its data members and base classes are destructed. The
> relevant clause, I believe, is 15.2, where it is said that partially
> constructed objects will have their fully constructed parts destroyed
> properly. It even mention that it works for arrays.

I don't have a copy of the standard, but I'll give you my
understanding of the language. Perhaps someone else who does have the
standard can settle this for us.

"Partially constructed objects" refers to exceptions that are thrown
by a constructor, not a destructor. The intent is for the language to
guarantee that objects the programmer cannot access are destroyed
(since the object has not yet finished constructing, the programmer
has not been able to obtain a reference to it).

If an exception is thrown from some function that is _not_ a
constructor, then only stack objects are destroyed. Therefore, if
A::~A above throws, since the B part is not a stack object, B::~B will
not be called.

I'd be interested in anybody else's opinion on this as well. I just
ran a quick test in CodeWarrior 7, and it seems to agree with my
interpretation.

> > But I think that ignoring the new exception is just as bad, but for
> > slightly different reasons. You still suffer from information loss
>
> No worse that with a try block in destructors, which is what is done
> now.

No, actually it is worse, because a try block in a destructor will not
prevent members and bases from being destroyed.

> > > Tell me, what do you do in your destructors to have them not throw?
> > > You put a try/catch. You catch everything, including divisions by zero
> > > and invalid memory accesses on some implementation (MSVC++). You loose
> > > information. That is the current best practice.
> >
> > You're ignoring the possibility of designing the class to not call
> > throwing operations in the destructor, which is the preferred method.
>
> OK, then I'll admit that it is the preferred method. But there is a
> nagging detail: what I would put in the destructor, the footer(), you
> have to put somewhere else, right? That somewhere else, in the face of
> an exception, must catch all exceptions, right?

Why? This doesn't follow at all. Presumably you're referring to code
like:

   header();
   body();
   footer();

Why is there a special need to catch exceptions thrown by footer()?

> So what is different
> here? Only the location of the code, not the behavior. Let's take a
> real life example:
>
> You have a resource, shared by many objects, that is ref-counted. Each
> object grabs it when it needs it and releases afterward. When the
> resources is released it may have to do some work on behalf of the
> other objects. The real resource here is a color table that must be
> written to a file. Accesses to the color table and file obviously have
> to be serialized this way. The concurrent accesses may be actually
> spread over many threads (so it is not simply a matter of reorganizing
> code).
>
> My code:
>
> {
>    Ref<Resource> ref( ColorTableManager.getCompressor() );
>    //... use color table, may throw.
> } // release happens here, exception may be swallowed.
>
>
> Your code (correct me if I'm wrong):
>
> {
>    Ref<Resource> ref( ColorTableManager.getCompressor() );
>    //... use color table.
>    ref.release(); // sets a flag to not do work in destructor.
> } // does release() in try block in destructor if flag not set.
>
> I'm very interested to see how you and other detractors would write
> that code. The point of the code example is: you have to release
> something that can fail. *Where* you do it is only a red herring, you
> have to do it anyway. The benefit of my code and proposal is that
> there is more automation (and one less flag).

Actually, here's my version:

{
   Ref<Resource> ref(ColorTableManager.getCompressor() );
   //... use color table, may throw.

   ref.WriteToFile(); // may throw.

} // destructor releases using non-throwing operations only.

I would separate the behavior so that destruction doesn't write to a
file or perform any throwing operations. Throwing operations would be
placed into a new function, which a client would be required to call
as above.

> > > My proposal just makes it automatic, language-enforced. You gain
> > > flexibility in the bargain because in the normal case (i.e.: not stack
> > > unwinding), your destructors will *not loose information*. So acording
> > > to you, my proposal is a gain. right? ;-)
> >
> > Not only is the exception lost, but the object that generated it goes
> > to The Twilight Zone, because it's neither alive nor dead. All legal
> > references to the object are gone, so nothing meaningful can be done
> > to correct the situation. That sounds like information loss to me.
>
> See above code example the discussion about 15.2 above. Prove me
> wrong.

I can't prove it, but you're wrong. ;-)

> > You're giving the programmer a function,
> > set_unwinding__resumption, and allowing him to call it at any time.
> > You can't hand-wave it away by saying that probably he'll call it once
> > at the beginning.
>
> I agree, it's the weak point of my proposal. Would it be more
> acceptable if the proposed behavior would always be enabled? That
> would not change the meaning of existing code, since it has really
> been properly tested and reviewed right? ;-). Seriously folks, would
> the proposal be more acceptable like that? I didn't say acceptable
> (yet), just more...

Actually, it would be a step in the right direction. But getting back
to the capsule summary line at the beginning of this post, it still
would not make exceptions exiting from destructors safe.

Bob

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: belvis@pacbell.net (Bob Bell)
Date: Thu, 26 Sep 2002 17:33:54 +0000 (UTC)
Raw View
pierrebai@hotmail.com (Pierre Baillargeon) wrote in message news:<6df0c6a8.0209240716.6fb5ec00@posting.google.com>...
> belvis@pacbell.net (Bob Bell) wrote in message news:<c87c1cfb.0209231924.6b639ab2@posting.google.com>...
> > pierrebai@hotmail.com (Pierre Baillargeon) wrote in message news:<6df0c6a8.0209230602.2aba3ca8@posting.google.com>...
> > > belvis@pacbell.net (Bob Bell) wrote in message news:<c87c1cfb.0209220939.46d823af@posting.google.com>...
> > > > pierrebai@hotmail.com (Pierre Baillargeon) wrote in message news:<6df0c6a8.0209201925.6faa9e57@posting.google.com>...
> > >
> >
> > I disagree here. Current best practice seems to do pretty well
> > already. My point is that you haven't yet established (to my
> > satisfaction anyway) that current best practice is insufficient.
>
> The current best practice (CBP) is sufficient for the current
> situation where the language makes it difficult to determine if it is
> safe to throw from the destructor. But anyway, I am very interested
> about what would be a convincing argument for you and the other
> detractors.

Your proposal doesn't make it safe to throw from a destructor. It just
prevents the program from calling std::terminate, which is somewhat
different. I guess that's the capsule summary of what's wrong with
this proposal.

> I agree that the main weakness of the proposal is the switchable
> behavior. I only proposed the switchable behavior to make the proposal
> more acceptable to people who like the current terminate() calls.
>
> I'm starting to believe that I was wrong on that point. Most
> detractors mentionned that point. What's more, I've just read Francis
> Glassborow's post in which he mention that he would like:
>
>    "> destructors never throw.
>       [snipped]
>     And I wish we could actually make that the default for dtors"
>
> Which I take it to mean that they would have an automatic invisible
> try/catch block. My proposal only difference is to allow the exception
> to escape the destructors when it could.

I can't speak for Francis, but I would interpret his remark as
"destructors should behave as if they were declared with a throw()
exception specification." In other words, any exception escaping a
destructor would lead to a call to std::unexpected. That's the
behavior I would like.

> > How do I test that a destructor will never throw? One simple way,
> > mentioned by you, is to wrap every destructor in try/catch. Another
> > way, mentioned by Francis in his reply, is don't call functions that
> > can throw. Usually, an inspection or review of the destructor code is
> > all that is needed to verify that the destructor does not call
> > throwing functions. This is viable because most destructors (that I
> > write anyway) are trivial compared to the rest of the program, and
> > usually do little more than release resources.
>
> It's all true, but a bit of a simplification, IMO. In code reviews,
> nothing in the call signature of a function tells you that it will
> throw or not. It's always a matter of memory and consistent
> maintenance to know that. Of course if you don't want your destructors
> to throw you will keep them doing the least possible work. I know that
> convincing people that expanding their usefulness is a hard battle.

Destructors really shouldn't do much work. They definately shouldn't
do things that can fail.

> > I agree with him that classes that can throw from destructors are
> > broken. Consider a class A with a base class B. A::~A runs and throws.
> > Not only is A::~A interrupted (which may cause it to leak resources,
> > etc.), but the B::~B never gets called!
> >
> > Note that your proposal doesn't make this situation better.
>
> I'd like to be corrected if I'm wrong, but I'm quite certain that if
> ~A() throws, all its data members and base classes are destructed. The
> relevant clause, I believe, is 15.2, where it is said that partially
> constructed objects will have their fully constructed parts destroyed
> properly. It even mention that it works for arrays.

I don't have a copy of the standard, but I'll give you my
understanding of the language. Perhaps someone else who does have the
standard can settle this for us.

"Partially constructed objects" refers to exceptions that are thrown
by a constructor, not a destructor. The intent is for the language to
guarantee that objects the programmer cannot access are destroyed
(since the object has not yet finished constructing, the programmer
has not been able to obtain a reference to it).

If an exception is thrown from some function that is _not_ a
constructor, then only stack objects are destroyed. Therefore, if
A::~A above throws, since the B part is not a stack object, B::~B will
not be called.

I'd be interested in anybody else's opinion on this as well. I just
ran a quick test in CodeWarrior 7, and it seems to agree with my
interpretation.

> > But I think that ignoring the new exception is just as bad, but for
> > slightly different reasons. You still suffer from information loss
>
> No worse that with a try block in destructors, which is what is done
> now.

No, actually it is worse, because a try block in a destructor will not
prevent members and bases from being destroyed.

> > and
> > resource leakage, but because the exception evaporates, there is
> > nothing that any part of the program can do about it. Imagine a
> > std::vector<Foo> being destroyed, where the destructor of the first
> > Foo throws, leaving every other Foo, and possibly the std::vector,
> > constructed and allocated, but leaked. And because the program doesn't
> > terminate, it still limps along in some unpredictable, inconsistent
> > state.
>
> Objects which throw in their destructors cannot be used in STL
> containers per the STL specification. My proposal doesn't change this,
> which means that objects that throw cannot be used with STL
> containers.
>
> I didn't want to include changes to the STL in my proposal to limit
> its scope. It would also be beyond my ability to correctly specify all
> needed changes.
>
> > > Tell me, what do you do in your destructors to have them not throw?
> > > You put a try/catch. You catch everything, including divisions by zero
> > > and invalid memory accesses on some implementation (MSVC++). You loose
> > > information. That is the current best practice.
> >
> > You're ignoring the possibility of designing the class to not call
> > throwing operations in the destructor, which is the preferred method.
>
> OK, then I'll admit that it is the preferred method. But there is a
> nagging detail: what I would put in the destructor, the footer(), you
> have to put somewhere else, right? That somewhere else, in the face of
> an exception, must catch all exceptions, right?

Why? This doesn't follow at all. Presumably you're referring to code
like:

   header();
   body();
   footer();

Why is there a special need to catch exceptions thrown by footer?

> So what is different
> here? Only the location of the code, not the behavior. Let's take a
> real life example:
>
> You have a resource, shared by many objects, that is ref-counted. Each
> object grabs it when it needs it and releases afterward. When the
> resources is released it may have to do some work on behalf of the
> other objects. The real resource here is a color table that must be
> written to a file. Accesses to the color table and file obviously have
> to be serialized this way. The concurrent accesses may be actually
> spread over many threads (so it is not simply a matter of reorganizing
> code).
>
> My code:
>
> {
>    Ref<Resource> ref( ColorTableManager.getCompressor() );
>    //... use color table, may throw.
> } // release happens here, exception may be swallowed.
>
>
> Your code (correct me if I'm wrong):
>
> {
>    Ref<Resource> ref( ColorTableManager.getCompressor() );
>    //... use color table.
>    ref.release(); // sets a flag to not do work in destructor.
> } // does release() in try block in destructor if flag not set.
>
> I'm very interested to see how you and other detractors would write
> that code. The point of the code example is: you have to release
> something that can fail. *Where* you do it is only a red herring, you
> have to do it anyway. The benefit of my code and proposal is that
> there is more automation (and one less flag).

Actually, here's my version:

{
   Ref<Resource> ref(ColorTableManager.getCompressor() );
   //... use color table, may throw.

   ref.WriteToFile(); // may throw.

} // destructor releases using non-throwing operations only.

I would separate the behavior so that destruction doesn't write to a
file or perform any throwing operations. Throwing operations would be
placed into a new function, which a client would be required to call
as above.

> > > My proposal just makes it automatic, language-enforced. You gain
> > > flexibility in the bargain because in the normal case (i.e.: not stack
> > > unwinding), your destructors will *not loose information*. So acording
> > > to you, my proposal is a gain. right? ;-)
> >
> > Not only is the exception lost, but the object that generated it goes
> > to The Twilight Zone, because it's neither alive nor dead. All legal
> > references to the object are gone, so nothing meaningful can be done
> > to correct the situation. That sounds like information loss to me.
>
> See above code example the discussion about 15.2 above. Prove me
> wrong.

I can't prove it, but you're wrong. ;-)

> > You're giving the programmer a function,
> > set_unwinding__resumption, and allowing him to call it at any time.
> > You can't hand-wave it away by saying that probably he'll call it once
> > at the beginning.
>
> I agree, it's the weak point of my proposal. Would it be more
> acceptable if the proposed behavior would always be enabled? That
> would not change the meaning of existing code, since it has really
> been properly tested and reviewed right? ;-). Seriously folks, would
> the proposal be more acceptable like that? I didn't say acceptable
> (yet), just more...

Actually, it would be a step in the right direction. But getting back
to the capsule summary line at the beginning of this post, it still
would not make exceptions from destructors safe.

Bob

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: francis.glassborow@ntlworld.com (Francis Glassborow)
Date: Thu, 26 Sep 2002 20:36:45 +0000 (UTC)
Raw View
In article <c87c1cfb.0209252107.70dc6f8e@posting.google.com>, Bob Bell
<belvis@pacbell.net> writes
>> I'd like to be corrected if I'm wrong, but I'm quite certain that if
>> ~A() throws, all its data members and base classes are destructed. The
>> relevant clause, I believe, is 15.2, where it is said that partially
>> constructed objects will have their fully constructed parts destroyed
>> properly. It even mention that it works for arrays.
>
>I don't have a copy of the standard, but I'll give you my
>understanding of the language. Perhaps someone else who does have the
>standard can settle this for us.
>
>"Partially constructed objects" refers to exceptions that are thrown
>by a constructor, not a destructor. The intent is for the language to
>guarantee that objects the programmer cannot access are destroyed
>(since the object has not yet finished constructing, the programmer
>has not been able to obtain a reference to it).
>
>If an exception is thrown from some function that is _not_ a
>constructor, then only stack objects are destroyed. Therefore, if
>A::~A above throws, since the B part is not a stack object, B::~B will
>not be called.
>
>I'd be interested in anybody else's opinion on this as well. I just
>ran a quick test in CodeWarrior 7, and it seems to agree with my
>interpretation.


I am not sure that the Standard actually deals with this. There quite a
number of corner problems with dtors which matter not at all until
people want to push the boundaries. But it matters not, because
supposing that the base subobject dtors are called all they do is make
matters worse by ensuring that you have a ghost object with some of its
limbs missing.

However I have just looked up the requisite part of the C++ Standard.
15.2 para 2 starts:

An object that is partially constructed or partially destroyed will have
destructors executed for all of its fully constructed subobjects, that
is, for subobjects for which the constructor has completed execution and
the destructor has not yet begun execution.

That makes it quite clear either CodeWarrior got it wrong or your test
is faulty. Note that the above requirement means that you could not even
resurrect the object as most of it is gone. I suppose that you could
place any pointers to any unreleased resources into container somewhere
for latter handling. But you could do that anyway without propagating an
exception out of a dtor.

I maintain that any dtor that _needs_ to throw is perverting the
semantics of destructors.  If it does not need to throw then it should
not do so under any circumstances.


--
Francis Glassborow      ACCU
64 Southfield Rd
Oxford OX4 1PA          +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: terekhov@web.de (Alexander Terekhov)
Date: Mon, 23 Sep 2002 20:37:36 +0000 (UTC)
Raw View
Niklas Matthies wrote:
[...]
> The only reason why never-throwing destructors are considered best
> practice today in C++ is that its exception handling mechanism wasn't
> designed to deal with more than one error at a time. I believe it's
> very reasonable to think about how this situation could be improved, so
> that less (or ideally no) information about errors gets lost.

struct Thrower{ ~Thrower() { throw "Trouble"; } /**/ };

int main()
{
  try {
    delete[] new Thrower[ MY_POOR_SALARY_IN_USD_WILL_BE_ENOUGH ];
  }
  catch(.../*this is my favorite C++ idiom, BTW*/) {
    // Do something with >>HOW MANY<< errors?
  }
}

regards,
alexander.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: belvis@pacbell.net (Bob Bell)
Date: Tue, 24 Sep 2002 03:43:53 +0000 (UTC)
Raw View
pierrebai@hotmail.com (Pierre Baillargeon) wrote in message news:<6df0c6a8.0209230602.2aba3ca8@posting.google.com>...
> belvis@pacbell.net (Bob Bell) wrote in message news:<c87c1cfb.0209220939.46d823af@posting.google.com>...
> > pierrebai@hotmail.com (Pierre Baillargeon) wrote in message news:<6df0c6a8.0209201925.6faa9e57@posting.google.com>...
> > >
> > > And what I'm saying is "what if we allowed it to be non-dangerous if
> > > desired". The original thread was about a change to the standard, not
> > > a discussion of current best practice.
> >
> > I would think that is appropriate. Any discussion of a change to the
> > standard should involve a discussion of current best practice, in
> > order to establish that, in some way, current best practice is not
> > good enough. This has not yet been established, so there is crucial
> > support missing from your proposal.
>
> Where I disagree with your point is that my proposal *changes* what
> would be considered best practice. And in fact I do illustrate where
> the current best practice fails:
>
> - In automating things that can fail.
> - In the difficulty of writing and testing that no destructor can ever
> throw.

I disagree here. Current best practice seems to do pretty well
already. My point is that you haven't yet established (to my
satisfaction anyway) that current best practice is insufficient.

Note that writing and testing that no destructor can ever throw" is on
the same order of difficulty of testing the exception handling
behavior of the program at large. In fact, I would even say that
testing the exception handling behavior is probably harder. So by
allowing destructors to throw, you're not really simplifying anything,
because now you have to test that every throw in a destructor is
properly handled at a catch point.

> > Your proposal seems to change what is currently a simple, easy to
> > understand, easy to follow rule into something complex and error
> > prone. I don't see the improvement.
>
> Where is the complexity? The current rule is easy to understand, easy
> to get wrong. Tell me: how do you actually test that no exception will
> ever be thrown from a destructor? The new rule is easy to understand:
> you can throw from a destructor. Where is the complexity?

Actually, the new rule you propose is "sometimes it's OK to throw from
a destructor, depending on the setting of a global flag." That's the
complexity: under this rule destructors may or may not be called in a
context in which throwing is OK. The current rule says "it's never OK
to throw from a destructor. Period." The current rule seems simpler to
me.

How do I test that a destructor will never throw? One simple way,
mentioned by you, is to wrap every destructor in try/catch. Another
way, mentioned by Francis in his reply, is don't call functions that
can throw. Usually, an inspection or review of the destructor code is
all that is needed to verify that the destructor does not call
throwing functions. This is viable because most destructors (that I
write anyway) are trivial compared to the rest of the program, and
usually do little more than release resources.

I agree with him that classes that can throw from destructors are
broken. Consider a class A with a base class B. A::~A runs and throws.
Not only is A::~A interrupted (which may cause it to leak resources,
etc.), but the B::~B never gets called!

Note that your proposal doesn't make this situation better.

> > You propose allowing an unhandled exception to be discarded and
> > replaced by an exception thrown in a destructor.
>
> This is incorrect. What I'm proposing is that the second exception is
> ignored. It is like putting a try block around all destructors when
> stack unwinding is under way. Which is *what you currently need to
> do*!

Oops! My bad. I misread your OP.

But I think that ignoring the new exception is just as bad, but for
slightly different reasons. You still suffer from information loss and
resource leakage, but because the exception evaporates, there is
nothing that any part of the program can do about it. Imagine a
std::vector<Foo> being destroyed, where the destructor of the first
Foo throws, leaving every other Foo, and possibly the std::vector,
constructed and allocated, but leaked. And because the program doesn't
terminate, it still limps along in some unpredictable, inconsistent
state.

> >  This seems like a
> > really bad idea, because you're saying that under some circumstances
> > it's OK to throw away information. If a low-level function throws an
> > exception that should go to a high-level caller, an exception in a
> > destructor would "short-circuit" that.
>
> Tell me, what do you do in your destructors to have them not throw?
> You put a try/catch. You catch everything, including divisions by zero
> and invalid memory accesses on some implementation (MSVC++). You loose
> information. That is the current best practice.

You're ignoring the possibility of designing the class to not call
throwing operations in the destructor, which is the preferred method.

I am also deliberately ignoring the possibility of divide by zero-type
exceptions -- basically, any exception that doesn't originate with a
throw statement. I've seen some pretty good discussions about why
these kinds of exceptions are not particularly useful, mainly since
they are not portable. The MSVC++ implementation is also broken in
that these exceptions are not accessible or catchable, which makes it
impossible to filter them out.

Personally, I regard conditions like divide by zero as a bug, which
should be fixed, not an expected condition that my exception
handler(s) should deal with.

> My proposal just makes it automatic, language-enforced. You gain
> flexibility in the bargain because in the normal case (i.e.: not stack
> unwinding), your destructors will *not loose information*. So acording
> to you, my proposal is a gain. right? ;-)

Not only is the exception lost, but the object that generated it goes
to The Twilight Zone, because it's neither alive nor dead. All legal
references to the object are gone, so nothing meaningful can be done
to correct the situation. That sounds like information loss to me.

> > Finally, you haven't simplified the implementation of destructors,
> > you've complicated it. Because now they may have to be sensitive to
> > whether or not throwing is "allowed." If not (if some higher-level
> > caller hasn't called set_unwinding_resumption(true)), the destructor
> > must absorb the exception and prevent its escape. So under some
> > circumstances this destructor reports errors, and under others it
> > doesn't? I don't know about you, but this would simply encourage me to
> > take the simpler road: destructors never throw.
>
> While this problem is true in theory, it won't be in practice, IMO.
>
> The first case, when it is set to true is no problem: old classes will
> go on using try blocks, and new ones will be happy.
>
> The second case. when it is set to false. The question is when will it
> be set to false? Not by old classes, since they are not aware of the
> existence of the function. So it will probably be managed by a
> singleton that keeps a ref-count counter to know when to set it back
> to false.
>
> The common case, I expect, will actually be that it is either never
> used or set to true once and for all in main() and in all statically
> created singletons that are constructed before main().

The biggest problem with your proposal is that a destructor _does not
know_ if it's OK to throw. It could be destroyed with the
unwinding_resumption flag set to false, in which case the destructor
should not throw. You're giving the programmer a function,
set_unwinding__resumption, and allowing him to call it at any time.
You can't hand-wave it away by saying that probably he'll call it once
at the beginning. A library class, for example, can't know what
context it will be used in. Perhaps I'll use the class in a project
that doesn't ever call set_unwinding_resumption, while in your project
you call set_unwinding_resumption(true) and expect destructors to
throw. Thus, the class must either be sensitive to whether throwing is
allowed, or simply take the easier way out: just don't throw.

Bob Bell

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: belvis@pacbell.net (Bob Bell)
Date: Tue, 24 Sep 2002 12:23:14 +0000 (UTC)
Raw View
news_comp.std.c++_expires-2002-10-01@nmhq.net (Niklas Matthies) wrote in message news:<slrnaouku5.43d.news_comp.std.c++_expires-2002-10-01@moongate.nmhq.net>...
> The only reason why never-throwing destructors are considered best
> practice today in C++ is that its exception handling mechanism wasn't
> designed to deal with more than one error at a time. I believe it's
> very reasonable to think about how this situation could be improved, so
> that less (or ideally no) information about errors gets lost.

So how do you propose that the language should handle multiple exceptions at once?

Bob

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: usenet@phatbasset.com ("Hillel Y. Sims")
Date: Tue, 24 Sep 2002 16:03:58 +0000 (UTC)
Raw View

"Pierre Baillargeon" <pierrebai@hotmail.com> wrote in message
news:6df0c6a8.0209230602.2aba3ca8@posting.google.com...
> Tell me: how do you actually test that no exception will
> ever be thrown from a destructor?

class MyType
{
  ~MyType() throw()
  {
     /*cleanup code that's not really supposed to throw*/
  }
};

--or--

(ref:
http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&frame=right&th=e
8754b0adf00544&seekm=5AUe9.47773%24rU2.1544247%40news4.srv.hcvlny.cv.net#lin
k1 )
class MyType
{
  ~MyType()
  {
     NOTHROW_REGION;
     /*cleanup code that's not really supposed to throw*/
  }
};

--
Hillel Y. Sims
FactSet Research Systems
hsims AT factset.com

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: kuyper@wizard.net ("James Russell Kuyper Jr.")
Date: Wed, 25 Sep 2002 11:42:46 +0000 (UTC)
Raw View
"Hillel Y. Sims" wrote:
>
> "Pierre Baillargeon" <pierrebai@hotmail.com> wrote in message
> news:6df0c6a8.0209230602.2aba3ca8@posting.google.com...
> > Tell me: how do you actually test that no exception will
> > ever be thrown from a destructor?
>
> class MyType
> {
>   ~MyType() throw()
>   {
>      /*cleanup code that's not really supposed to throw*/
>   }
> };

Well, yes. :-)
But I presume the intention was to have the destructor actually return
without throwing.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: nagle@animats.com (John Nagle)
Date: Wed, 25 Sep 2002 15:32:37 +0000 (UTC)
Raw View
    There's an old argument that exception specifications
should be enforced at compile time.  This is easy to do.
The rule is simply that you can't call anything with a
less restrictive exception specification than yours,
unless it's protected by a try/catch block.  And, of course,
you can't directly raise something not in your exception
specification.

    It's a pain to retrofit to existing code, but it's sound.
Except for the retrofit problem, it would work out well.
Most likely, everybody would derive their exceptions
from std::exception, so that everything would just have
a spec of either "throw(std::exception)" or "throw()".

    Suppose you had that.  Then, what should be the
default exception specification for destructors?
"throw()"?

    John Nagle
    Animats

Bob Bell wrote:

> pierrebai@hotmail.com (Pierre Baillargeon) wrote in message news:<6df0c6a8.0209230602.2aba3ca8@posting.google.com>...
>
>>belvis@pacbell.net (Bob Bell) wrote in message news:<c87c1cfb.0209220939.46d823af@posting.google.com>...
>>
>>>pierrebai@hotmail.com (Pierre Baillargeon) wrote in message news:<6df0c6a8.0209201925.6faa9e57@posting.google.com>...
>>>
>>>>And what I'm saying is "what if we allowed it to be non-dangerous if
>>>>desired". The original thread was about a change to the standard, not
>>>>a discussion of current best practice.
>>>>
>>>I would think that is appropriate. Any discussion of a change to the
>>>standard should involve a discussion of current best practice, in
>>>order to establish that, in some way, current best practice is not
>>>good enough. This has not yet been established, so there is crucial
>>>support missing from your proposal.
>>>
>>Where I disagree with your point is that my proposal *changes* what
>>would be considered best practice. And in fact I do illustrate where
>>the current best practice fails:
>>
>>- In automating things that can fail.
>>- In the difficulty of writing and testing that no destructor can ever
>>throw.
>>
>
> I disagree here. Current best practice seems to do pretty well
> already. My point is that you haven't yet established (to my
> satisfaction anyway) that current best practice is insufficient.
>
> Note that writing and testing that no destructor can ever throw" is on
> the same order of difficulty of testing the exception handling
> behavior of the program at large. In fact, I would even say that
> testing the exception handling behavior is probably harder. So by
> allowing destructors to throw, you're not really simplifying anything,
> because now you have to test that every throw in a destructor is
> properly handled at a catch point.
>
>
>>>Your proposal seems to change what is currently a simple, easy to
>>>understand, easy to follow rule into something complex and error
>>>prone. I don't see the improvement.
>>>
>>Where is the complexity? The current rule is easy to understand, easy
>>to get wrong. Tell me: how do you actually test that no exception will
>>ever be thrown from a destructor? The new rule is easy to understand:
>>you can throw from a destructor. Where is the complexity?
>>
>
> Actually, the new rule you propose is "sometimes it's OK to throw from
> a destructor, depending on the setting of a global flag." That's the
> complexity: under this rule destructors may or may not be called in a
> context in which throwing is OK. The current rule says "it's never OK
> to throw from a destructor. Period." The current rule seems simpler to
> me.
>
> How do I test that a destructor will never throw? One simple way,
> mentioned by you, is to wrap every destructor in try/catch. Another
> way, mentioned by Francis in his reply, is don't call functions that
> can throw. Usually, an inspection or review of the destructor code is
> all that is needed to verify that the destructor does not call
> throwing functions. This is viable because most destructors (that I
> write anyway) are trivial compared to the rest of the program, and
> usually do little more than release resources.
>
> I agree with him that classes that can throw from destructors are
> broken. Consider a class A with a base class B. A::~A runs and throws.
> Not only is A::~A interrupted (which may cause it to leak resources,
> etc.), but the B::~B never gets called!
>
> Note that your proposal doesn't make this situation better.
>
>
>>>You propose allowing an unhandled exception to be discarded and
>>>replaced by an exception thrown in a destructor.
>>>
>>This is incorrect. What I'm proposing is that the second exception is
>>ignored. It is like putting a try block around all destructors when
>>stack unwinding is under way. Which is *what you currently need to
>>do*!
>>
>
> Oops! My bad. I misread your OP.
>
> But I think that ignoring the new exception is just as bad, but for
> slightly different reasons. You still suffer from information loss and
> resource leakage, but because the exception evaporates, there is
> nothing that any part of the program can do about it. Imagine a
> std::vector<Foo> being destroyed, where the destructor of the first
> Foo throws, leaving every other Foo, and possibly the std::vector,
> constructed and allocated, but leaked. And because the program doesn't
> terminate, it still limps along in some unpredictable, inconsistent
> state.
>
>
>>> This seems like a
>>>really bad idea, because you're saying that under some circumstances
>>>it's OK to throw away information. If a low-level function throws an
>>>exception that should go to a high-level caller, an exception in a
>>>destructor would "short-circuit" that.
>>>
>>Tell me, what do you do in your destructors to have them not throw?
>>You put a try/catch. You catch everything, including divisions by zero
>>and invalid memory accesses on some implementation (MSVC++). You loose
>>information. That is the current best practice.
>>
>
> You're ignoring the possibility of designing the class to not call
> throwing operations in the destructor, which is the preferred method.
>
> I am also deliberately ignoring the possibility of divide by zero-type
> exceptions -- basically, any exception that doesn't originate with a
> throw statement. I've seen some pretty good discussions about why
> these kinds of exceptions are not particularly useful, mainly since
> they are not portable. The MSVC++ implementation is also broken in
> that these exceptions are not accessible or catchable, which makes it
> impossible to filter them out.
>
> Personally, I regard conditions like divide by zero as a bug, which
> should be fixed, not an expected condition that my exception
> handler(s) should deal with.
>
>
>>My proposal just makes it automatic, language-enforced. You gain
>>flexibility in the bargain because in the normal case (i.e.: not stack
>>unwinding), your destructors will *not loose information*. So acording
>>to you, my proposal is a gain. right? ;-)
>>
>
> Not only is the exception lost, but the object that generated it goes
> to The Twilight Zone, because it's neither alive nor dead. All legal
> references to the object are gone, so nothing meaningful can be done
> to correct the situation. That sounds like information loss to me.
>
>
>>>Finally, you haven't simplified the implementation of destructors,
>>>you've complicated it. Because now they may have to be sensitive to
>>>whether or not throwing is "allowed." If not (if some higher-level
>>>caller hasn't called set_unwinding_resumption(true)), the destructor
>>>must absorb the exception and prevent its escape. So under some
>>>circumstances this destructor reports errors, and under others it
>>>doesn't? I don't know about you, but this would simply encourage me to
>>>take the simpler road: destructors never throw.
>>>
>>While this problem is true in theory, it won't be in practice, IMO.
>>
>>The first case, when it is set to true is no problem: old classes will
>>go on using try blocks, and new ones will be happy.
>>
>>The second case. when it is set to false. The question is when will it
>>be set to false? Not by old classes, since they are not aware of the
>>existence of the function. So it will probably be managed by a
>>singleton that keeps a ref-count counter to know when to set it back
>>to false.
>>
>>The common case, I expect, will actually be that it is either never
>>used or set to true once and for all in main() and in all statically
>>created singletons that are constructed before main().
>>
>
> The biggest problem with your proposal is that a destructor _does not
> know_ if it's OK to throw. It could be destroyed with the
> unwinding_resumption flag set to false, in which case the destructor
> should not throw. You're giving the programmer a function,
> set_unwinding__resumption, and allowing him to call it at any time.
> You can't hand-wave it away by saying that probably he'll call it once
> at the beginning. A library class, for example, can't know what
> context it will be used in. Perhaps I'll use the class in a project
> that doesn't ever call set_unwinding_resumption, while in your project
> you call set_unwinding_resumption(true) and expect destructors to
> throw. Thus, the class must either be sensitive to whether throwing is
> allowed, or simply take the easier way out: just don't throw.
>
> Bob Bell
>
> ---
> [ comp.std.c++ is moderated.  To submit articles, try just posting with ]
> [ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
> [              --- Please see the FAQ before posting. ---               ]
> [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]
>
>

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: pierrebai@hotmail.com (Pierre Baillargeon)
Date: Wed, 25 Sep 2002 16:02:51 +0000 (UTC)
Raw View
belvis@pacbell.net (Bob Bell) wrote in message news:<c87c1cfb.0209231924.6b639ab2@posting.google.com>...
> pierrebai@hotmail.com (Pierre Baillargeon) wrote in message news:<6df0c6a8.0209230602.2aba3ca8@posting.google.com>...
> > belvis@pacbell.net (Bob Bell) wrote in message news:<c87c1cfb.0209220939.46d823af@posting.google.com>...
> > > pierrebai@hotmail.com (Pierre Baillargeon) wrote in message news:<6df0c6a8.0209201925.6faa9e57@posting.google.com>...
> >
>
> I disagree here. Current best practice seems to do pretty well
> already. My point is that you haven't yet established (to my
> satisfaction anyway) that current best practice is insufficient.

The current best practice (CBP) is sufficient for the current
situation where the language makes it difficult to determine if it is
safe to throw from the destructor. But anyway, I am very interested
about what would be a convincing argument for you and the other
detractors.

> Note that writing and testing that no destructor can ever throw" is on
> the same order of difficulty of testing the exception handling
> behavior of the program at large. In fact, I would even say that
> testing the exception handling behavior is probably harder. So by
> allowing destructors to throw, you're not really simplifying anything,
> because now you have to test that every throw in a destructor is
> properly handled at a catch point.

It's hard to debate which is simpler or harder: unless we have hard
data, it's only a matter of past experience ands opinions. Because I
use classic RAII extensively, I actually don't worry much about
exceptions. The programs usually have a few nexus of try/catch in the
main distpatch points. So in my experience, not having to worry about
destructors is easier, it's one less worry.

> Actually, the new rule you propose is "sometimes it's OK to throw from
> a destructor, depending on the setting of a global flag." That's the
> complexity: under this rule destructors may or may not be called in a
> context in which throwing is OK. The current rule says "it's never OK
> to throw from a destructor. Period." The current rule seems simpler to
> me.

I agree that the main weakness of the proposal is the switchable
behavior. I only proposed the switchable behavior to make the proposal
more acceptable to people who like the current terminate() calls.

I'm starting to believe that I was wrong on that point. Most
detractors mentionned that point. What's more, I've just read Francis
Glassborow's post in which he mention that he would like:

   "> destructors never throw.
      [snipped]
    And I wish we could actually make that the default for dtors"

Which I take it to mean that they would have an automatic invisible
try/catch block. My proposal only difference is to allow the exception
to escape the destructors when it could.

> How do I test that a destructor will never throw? One simple way,
> mentioned by you, is to wrap every destructor in try/catch. Another
> way, mentioned by Francis in his reply, is don't call functions that
> can throw. Usually, an inspection or review of the destructor code is
> all that is needed to verify that the destructor does not call
> throwing functions. This is viable because most destructors (that I
> write anyway) are trivial compared to the rest of the program, and
> usually do little more than release resources.

It's all true, but a bit of a simplification, IMO. In code reviews,
nothing in the call signature of a function tells you that it will
throw or not. It's always a matter of memory and consistent
maintenance to know that. Of course if you don't want your destructors
to throw you will keep them doing the least possible work. I know that
convincing people that expanding their usefulness is a hard battle.

> I agree with him that classes that can throw from destructors are
> broken. Consider a class A with a base class B. A::~A runs and throws.
> Not only is A::~A interrupted (which may cause it to leak resources,
> etc.), but the B::~B never gets called!
>
> Note that your proposal doesn't make this situation better.

I'd like to be corrected if I'm wrong, but I'm quite certain that if
~A() throws, all its data members and base classes are destructed. The
relevant clause, I believe, is 15.2, where it is said that partially
constructed objects will have their fully constructed parts destroyed
properly. It even mention that it works for arrays.

> But I think that ignoring the new exception is just as bad, but for
> slightly different reasons. You still suffer from information loss

No worse that with a try block in destructors, which is what is done
now.

> and
> resource leakage, but because the exception evaporates, there is
> nothing that any part of the program can do about it. Imagine a
> std::vector<Foo> being destroyed, where the destructor of the first
> Foo throws, leaving every other Foo, and possibly the std::vector,
> constructed and allocated, but leaked. And because the program doesn't
> terminate, it still limps along in some unpredictable, inconsistent
> state.

Objects which throw in their destructors cannot be used in STL
containers per the STL specification. My proposal doesn't change this,
which means that objects that throw cannot be used with STL
containers.

I didn't want to include changes to the STL in my proposal to limit
its scope. It would also be beyond my ability to correctly specify all
needed changes.

> > Tell me, what do you do in your destructors to have them not throw?
> > You put a try/catch. You catch everything, including divisions by zero
> > and invalid memory accesses on some implementation (MSVC++). You loose
> > information. That is the current best practice.
>
> You're ignoring the possibility of designing the class to not call
> throwing operations in the destructor, which is the preferred method.

OK, then I'll admit that it is the preferred method. But there is a
nagging detail: what I would put in the destructor, the footer(), you
have to put somewhere else, right? That somewhere else, in the face of
an exception, must catch all exceptions, right? So what is different
here? Only the location of the code, not the behavior. Let's take a
real life example:

You have a resource, shared by many objects, that is ref-counted. Each
object grabs it when it needs it and releases afterward. When the
resources is released it may have to do some work on behalf of the
other objects. The real resource here is a color table that must be
written to a file. Accesses to the color table and file obviously have
to be serialized this way. The concurrent accesses may be actually
spread over many threads (so it is not simply a matter of reorganizing
code).

My code:

{
   Ref<Resource> ref( ColorTableManager.getCompressor() );
   //... use color table, may throw.
} // release happens here, exception may be swallowed.


Your code (correct me if I'm wrong):

{
   Ref<Resource> ref( ColorTableManager.getCompressor() );
   //... use color table.
   ref.release(); // sets a flag to not do work in destructor.
} // does release() in try block in destructor if flag not set.

I'm very interested to see how you and other detractors would write
that code. The point of the code example is: you have to release
something that can fail. *Where* you do it is only a red herring, you
have to do it anyway. The benefit of my code and proposal is that
there is more automation (and one less flag).

> > My proposal just makes it automatic, language-enforced. You gain
> > flexibility in the bargain because in the normal case (i.e.: not stack
> > unwinding), your destructors will *not loose information*. So acording
> > to you, my proposal is a gain. right? ;-)
>
> Not only is the exception lost, but the object that generated it goes
> to The Twilight Zone, because it's neither alive nor dead. All legal
> references to the object are gone, so nothing meaningful can be done
> to correct the situation. That sounds like information loss to me.

See above code example the discussion about 15.2 above. Prove me
wrong.

> You're giving the programmer a function,
> set_unwinding__resumption, and allowing him to call it at any time.
> You can't hand-wave it away by saying that probably he'll call it once
> at the beginning.

I agree, it's the weak point of my proposal. Would it be more
acceptable if the proposed behavior would always be enabled? That
would not change the meaning of existing code, since it has really
been properly tested and reviewed right? ;-). Seriously folks, would
the proposal be more acceptable like that? I didn't say acceptable
(yet), just more...

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: francis.glassborow@ntlworld.com (Francis Glassborow)
Date: Wed, 25 Sep 2002 17:24:59 +0000 (UTC)
Raw View
In article <6df0c6a8.0209240716.6fb5ec00@posting.google.com>, Pierre
Baillargeon <pierrebai@hotmail.com> writes
>OK, then I'll admit that it is the preferred method. But there is a
>nagging detail: what I would put in the destructor, the footer(), you
>have to put somewhere else, right? That somewhere else, in the face of
>an exception, must catch all exceptions, right? So what is different
>here? Only the location of the code, not the behavior. Let's take a
>real life example:

The fundamental difference is that anywhere else leaves you with a
'live' object. Once a dtor starts running the object is dead, it is
gone, unless you are going to propose resurrecting ctors which, IMHO,
would be a complete mess.

--
Francis Glassborow      ACCU
64 Southfield Rd
Oxford OX4 1PA          +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: belvis@pacbell.net (Bob Bell)
Date: Mon, 23 Sep 2002 05:19:23 +0000 (UTC)
Raw View
pierrebai@hotmail.com (Pierre Baillargeon) wrote in message news:<6df0c6a8.0209201925.6faa9e57@posting.google.com>...
> glancaster@ntlworld.com ("Garry Lancaster") wrote in message news:<RW_h9.899$nU2.53545@newsfep1-gui.server.ntli.net>...
> > Pierre Baillargeon:
> >
> > No, what I and others are actually saying is "it's
> > (generally) dangerous to throw from destructors, so
> > (generally) don't throw from destructors".
>
> And what I'm saying is "what if we allowed it to be non-dangerous if
> desired". The original thread was about a change to the standard, not
> a discussion of current best practice.

I would think that is appropriate. Any discussion of a change to the
standard should involve a discussion of current best practice, in
order to establish that, in some way, current best practice is not
good enough. This has not yet been established, so there is crucial
support missing from your proposal.

Your proposal seems to change what is currently a simple, easy to
understand, easy to follow rule into something complex and error
prone. I don't see the improvement.

You propose allowing an unhandled exception to be discarded and
replaced by an exception thrown in a destructor. This seems like a
really bad idea, because you're saying that under some circumstances
it's OK to throw away information. If a low-level function throws an
exception that should go to a high-level caller, an exception in a
destructor would "short-circuit" that.

In fact, it seems that an exception could trigger a cascade of
destructor exceptions: in some low-level function a std::bad_alloc is
thrown. During unwinding, an A object's destructor throws an X. During
unwinding for that, a B throws a Y.

At this point, three exceptions have been thrown, only the last one of
which will actually get to a catch handler. Not only will the caller
not know about (and be able to respond to if it wants) the allocation
failure, it will not know that the A destruction failed. This cascade
can go on indefinately, so how many failures will be unreported?

Also, when the new exception replaces the old one, presumably the old
exception will be destroyed. What happens if that destruction throws
an exception? I suppose that the answer depends on when that
destruction takes place, and it may seem like an obscure case, but
this is your proposal, and it should go all the way down. What
happens?

Finally, you haven't simplified the implementation of destructors,
you've complicated it. Because now they may have to be sensitive to
whether or not throwing is "allowed." If not (if some higher-level
caller hasn't called set_unwinding_resumption(true)), the destructor
must absorb the exception and prevent its escape. So under some
circumstances this destructor reports errors, and under others it
doesn't? I don't know about you, but this would simply encourage me to
take the simpler road: destructors never throw.

Bob

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: francis.glassborow@ntlworld.com (Francis Glassborow)
Date: Mon, 23 Sep 2002 16:20:22 +0000 (UTC)
Raw View
In article <c87c1cfb.0209220939.46d823af@posting.google.com>, Bob Bell
<belvis@pacbell.net> writes
>Finally, you haven't simplified the implementation of destructors,
>you've complicated it. Because now they may have to be sensitive to
>whether or not throwing is "allowed." If not (if some higher-level
>caller hasn't called set_unwinding_resumption(true)), the destructor
>must absorb the exception and prevent its escape. So under some
>circumstances this destructor reports errors, and under others it
>doesn't? I don't know about you, but this would simply encourage me to
>take the simpler road: destructors never throw.


And I wish we could actually make that the default for dtors (even
though for legacy reasons we cannot make it for other functions). Yes, I
know all the objections so it will not happen.

--
Francis Glassborow      ACCU
64 Southfield Rd
Oxford OX4 1PA          +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: pierrebai@hotmail.com (Pierre Baillargeon)
Date: Mon, 23 Sep 2002 16:20:45 +0000 (UTC)
Raw View
belvis@pacbell.net (Bob Bell) wrote in message news:<c87c1cfb.0209220939.46d823af@posting.google.com>...
> pierrebai@hotmail.com (Pierre Baillargeon) wrote in message news:<6df0c6a8.0209201925.6faa9e57@posting.google.com>...
> >
> > And what I'm saying is "what if we allowed it to be non-dangerous if
> > desired". The original thread was about a change to the standard, not
> > a discussion of current best practice.
>
> I would think that is appropriate. Any discussion of a change to the
> standard should involve a discussion of current best practice, in
> order to establish that, in some way, current best practice is not
> good enough. This has not yet been established, so there is crucial
> support missing from your proposal.

Where I disagree with your point is that my proposal *changes* what
would be considered best practice. And in fact I do illustrate where
the current best practice fails:

- In automating things that can fail.
- In the difficulty of writing and testing that no destructor can ever
throw.

> Your proposal seems to change what is currently a simple, easy to
> understand, easy to follow rule into something complex and error
> prone. I don't see the improvement.

Where is the complexity? The current rule is easy to understand, easy
to get wrong. Tell me: how do you actually test that no exception will
ever be thrown from a destructor? The new rule is easy to understand:
you can throw from a destructor. Where is the complexity?

> You propose allowing an unhandled exception to be discarded and
> replaced by an exception thrown in a destructor.

This is incorrect. What I'm proposing is that the second exception is
ignored. It is like putting a try block around all destructors when
stack unwinding is under way. Which is *what you currently need to
do*!

>  This seems like a
> really bad idea, because you're saying that under some circumstances
> it's OK to throw away information. If a low-level function throws an
> exception that should go to a high-level caller, an exception in a
> destructor would "short-circuit" that.

Tell me, what do you do in your destructors to have them not throw?
You put a try/catch. You catch everything, including divisions by zero
and invalid memory accesses on some implementation (MSVC++). You loose
information. That is the current best practice.

My proposal just makes it automatic, language-enforced. You gain
flexibility in the bargain because in the normal case (i.e.: not stack
unwinding), your destructors will *not loose information*. So acording
to you, my proposal is a gain. right? ;-)

> In fact, it seems that an exception could trigger a cascade of
> destructor exceptions: in some low-level function a std::bad_alloc is
> thrown. During unwinding, an A object's destructor throws an X. During
> unwinding for that, a B throws a Y.

No more than what it already going on. If your destructor needs an A
and a B, what are you doing now? Using a try block and swallowing the
exception, or terminate().

> At this point, three exceptions have been thrown, only the last one of
> which will actually get to a catch handler. Not only will the caller
> not know about (and be able to respond to if it wants) the allocation
> failure, it will not know that the A destruction failed. This cascade
> can go on indefinately, so how many failures will be unreported?

The first part your affirmation is wrong, as I've pointed out before.
The second, also, since current best practice is to ignore additional
exceptions anyway.

> Also, when the new exception replaces the old one, presumably the old
> exception will be destroyed. What happens if that destruction throws
> an exception? I suppose that the answer depends on when that
> destruction takes place, and it may seem like an obscure case, but
> this is your proposal, and it should go all the way down. What
> happens?

The first part is false, so the second doesn't hold. As for successive
ignored exceptions, before being thrown they are an automatic object
and will be destroyed like other automatic objects.

A real problem, that I've actually seen in many libraries and systems,
is exception classes that contains a std::string or MFC CString. Which
may allocate memory when copied during the exception throw process.
And sometimes, they are even catched by value! My proposal won't solve
these errors, BTW, since it concerns only things done during
destruction of stack objects, not the reception of the exception
objects.

> Finally, you haven't simplified the implementation of destructors,
> you've complicated it. Because now they may have to be sensitive to
> whether or not throwing is "allowed." If not (if some higher-level
> caller hasn't called set_unwinding_resumption(true)), the destructor
> must absorb the exception and prevent its escape. So under some
> circumstances this destructor reports errors, and under others it
> doesn't? I don't know about you, but this would simply encourage me to
> take the simpler road: destructors never throw.

While this problem is true in theory, it won't be in practice, IMO.

The first case, when it is set to true is no problem: old classes will
go on using try blocks, and new ones will be happy.

The second case. when it is set to false. The question is when will it
be set to false? Not by old classes, since they are not aware of the
existence of the function. So it will probably be managed by a
singleton that keeps a ref-count counter to know when to set it back
to false.

The common case, I expect, will actually be that it is either never
used or set to true once and for all in main() and in all statically
created singletons that are constructed before main().

--
Pierre B.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: francis.glassborow@ntlworld.com (Francis Glassborow)
Date: Mon, 23 Sep 2002 17:05:47 +0000 (UTC)
Raw View
In article <6df0c6a8.0209230602.2aba3ca8@posting.google.com>, Pierre
Baillargeon <pierrebai@hotmail.com> writes
>Tell me, what do you do in your destructors to have them not throw?

I simply do not call functions that can throw. If I am mistaken my code
is clearly seriously broken, a fact that I would want to know about
rather than letting it carry on and reek havoc.


--
Francis Glassborow      ACCU
64 Southfield Rd
Oxford OX4 1PA          +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: news_comp.std.c++_expires-2002-10-01@nmhq.net (Niklas Matthies)
Date: Mon, 23 Sep 2002 18:29:14 +0000 (UTC)
Raw View
On Mon, 23 Sep 2002 17:05:47 +0000 (UTC), Francis Glassborow <francis.glassborow@ntlworld.com> wrote:
>  In article <6df0c6a8.0209230602.2aba3ca8@posting.google.com>, Pierre
>  Baillargeon <pierrebai@hotmail.com> writes
> >Tell me, what do you do in your destructors to have them not throw?
>
>  I simply do not call functions that can throw.

Releasing many but the most trivial resources generally can result in an
error (take file handles for a very common example). What you are saying
is that such resources shouldn't be handled by RAII, or even when not
handled by RAII, shouldn't be handled while an exception is pending
(e.g. within a rethrowing catch block).

This is a reasoning I find highly questionable. Conceptually, it is
perfectly sound to have situations where errors are accumulating before
you can handle any of them, and it's not generally true that only the
first one that happens to be thrown is the one of interest.

The only reason why never-throwing destructors are considered best
practice today in C++ is that its exception handling mechanism wasn't
designed to deal with more than one error at a time. I believe it's
very reasonable to think about how this situation could be improved, so
that less (or ideally no) information about errors gets lost.

-- Niklas Matthies
--
Help! The paranoids are after me!

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: pierrebai@hotmail.com (Pierre Baillargeon)
Date: Sat, 21 Sep 2002 19:08:08 +0000 (UTC)
Raw View
glancaster@ntlworld.com ("Garry Lancaster") wrote in message news:<RW_h9.899$nU2.53545@newsfep1-gui.server.ntli.net>...
> Pierre Baillargeon:
>
> No, what I and others are actually saying is "it's
> (generally) dangerous to throw from destructors, so
> (generally) don't throw from destructors".

And what I'm saying is "what if we allowed it to be non-dangerous if
desired". The original thread was about a change to the standard, not
a discussion of current best practice.

> We don't need to change the language to get
> your code to do the right thing in the face of
> exceptions (which is probably why we have
> moved away from discussing your proposed
> language change). We just need to change
> your code.

My proposal is not about making my code do the right thing: it already
does. But my code sometimes blocks exceptions when it would have been
possible to throw, because I don't want to keep an exception-state
variable in all my classes. The proposal is about:

- Simplifying that situation.
- Removing an obscure bug that abrutly ends a program, that I claim
most C++ programmers don't even know exists and which is easy to get
wrong by a single possibly-throwing call in a destructor.
- Allowing error reporting from a destructor using exception, since
the fear of termination no longer exists.
- Which in turn allow more things, which need to be done/undone, to be
automated using RAII-like classes.

> > All suggestions always have to do with putting the header/footer pair
> > in a function with the body either a virtual function or templated.
>
> Not true. See Allan W.'s more straightforward
> refactoring in the message you are replying to.
> It doesn't do the function bracketing you are
> after, but it does work.

When I showed the skeleton of my code design, at your request, it was
to illustrate the usage of a RAII-like class that can throw in a
destructor, to show why I wanted to be able to throw from a destructor
safely. It was also to part of my claim that those RAII-like classes
*because of their automation* saved me a lot of headache.

Removing the automation solves the exception-from-destructor problem
by simply bringing back the orginal problem of having to call footers
manually.

> > They all fail to address the usability problems.
>
> As already discussed, functional decomposition,
> which is the umbrella term for most of your gripes
> when stripped of editorialising, is generally a
> good thing. But of course can be overdone.

Is this an agreement to my points? Or just, when striped of
editorialising,  pontification?

> > Some claim that the destructor will somehow corrupt the data,
>
> The "claim" is simply that if a destructor throws,
> when the destructor was itself called by the
> exception handling stack unwinding mechanism,
> the program will terminate. Maybe you doubt this,
> but the standard is quite clear. 15.2/3 reads:

Umph. Isn't this exactly what I wrote in my orignal post and the exact
reason why I called for a change? IOW, once again you seem to be
agreeing with me now.

> > but it
> > can be avoided *easily* with a single if.
>
> std::uncaught_exception *again*?

So you agree with me that a change is needed, right ;). Seriously, I
don't follow your point: If I really want to avoid doing the footer
when the body failed, how does that generate an
std::uncaught_exception?

> > I don't only because I don't
> > need that capability.
>
> This is burying your head in the sand.
> You may have reservations about the alternatives
> offered so far, but at least they all work. At the
> moment your code doesn't work, at least when
> you consider its behaviour in the face of
> exceptions (which you almost certainly should).

I already told you in my september 5th post that my code use exception
filtering. IOW, I already do check before letting exceptions escape my
destructors, but I have to write that check in all destructors. And as
I said, that means that some destructors that could have thrown
(because they really were inside another try block) don't, because (as
I already said in this post in in earlier ones, sigh) I would have to
keep a flag in all instances of all classes.


> The simple rule is: start with something that
> works, then improve it.
>
> Another simple rule: before you can solve a
> problem you have to admit that you have one.
> Until you do this, you will probably continue to
> find the thread "pointless".

I am not allowed to reply to this by the comp.std.c++ chart.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: glancaster@ntlworld.com ("Garry Lancaster")
Date: Wed, 18 Sep 2002 16:17:57 +0000 (UTC)
Raw View
Pierre Baillargeon:
> Frankly, I'm starting to find this thread pointless.

Well, if we're *boring* you, don't post anymore ;-)
But if you keep posting, people are going to keep
replying, if only to correct mistakes and
misunderstandings.

> All replies go
> along the same lines: it's dangerous to throw from destructors, so
> don't do anything in destructors.

No, what I and others are actually saying is "it's
(generally) dangerous to throw from destructors, so
(generally) don't throw from destructors".

(I use the weasel word "generally" because there
are some cases where throwing from destructors
is not dangerous, but as far as I can tell your code
isn't one of these cases. This is the same weasel
word the standard uses for the same advice, so at
least I'm in good company ;-)

> No one considers the alternative: if
> it were not dangerous to throw from destructors we could do useful
> things in destructors.

We can already do useful things from destructors.
We just can't (generally) throw.

We don't need to change the language to get
your code to do the right thing in the face of
exceptions (which is probably why we have
moved away from discussing your proposed
language change). We just need to change
your code.

> All suggestions always have to do with putting the header/footer pair
> in a function with the body either a virtual function or templated.

Not true. See Allan W.'s more straightforward
refactoring in the message you are replying to.
It doesn't do the function bracketing you are
after, but it does work.

> They all fail to address the usability problems.
> The usability problems are:
>
> - Having to put code in a separate function/object/class.
> - Not having access to local variables.
> - Spreading the code in many places, making it harder to
> read/understand.
> - One-use code forced to be put in a reusable function/object/class.
> - Harder to nest multiple levels of arbitrary headers/footers.

As already discussed, functional decomposition,
which is the umbrella term for most of your gripes
when stripped of editorialising, is generally a
good thing. But of course can be overdone.

> Some claim that the destructor will somehow corrupt the data,

The "claim" is simply that if a destructor throws,
when the destructor was itself called by the
exception handling stack unwinding mechanism,
the program will terminate. Maybe you doubt this,
but the standard is quite clear. 15.2/3 reads:

"The process of calling destructors for automatic objects
constructed on the path from a try block to a
throw-expression is called 'stack unwinding.' [Note: If
a destructor called during stack unwinding exits
with an exception, terminate is called (15.5.1). So
destructors should generally catch exceptions and
not let them propagate out of the destructor. -end note]"

> but it
> can be avoided *easily* with a single if.

std::uncaught_exception *again*?

> I don't only because I don't
> need that capability.

This is burying your head in the sand.
You may have reservations about the alternatives
offered so far, but at least they all work. At the
moment your code doesn't work, at least when
you consider its behaviour in the face of
exceptions (which you almost certainly should).

The simple rule is: start with something that
works, then improve it.

Another simple rule: before you can solve a
problem you have to admit that you have one.
Until you do this, you will probably continue to
find the thread "pointless".

Kind regards

Garry Lancaster
Codemill Ltd
Visit our web site at http://www.codemill.net

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: musiphil@bawi.org (Sungbom Kim)
Date: Tue, 17 Sep 2002 12:01:52 +0000 (UTC)
Raw View
Allan W wrote:
>
> Because a tool can be abused, doesn't make it a bad tool -- in fact,
> any tool can be abused. (Do you know how dangerous a for()-loop is?
> How about the "break" statement?)
>
> A footer is not a "Resource." The RAII (Resource Aquisition Is
> Initialization) coding technique is not an appropriate way to generate it.

But isn't it an idiom that is common and useful enough?
Stroustrup also presented such a technique in his article
"Wrapping C++ Member Function Calls"
<http://www.research.att.com/~bs/wrapper.pdf>.

--
Sungbom Kim <musiphil@bawi.org>

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: pierrebai@hotmail.com (Pierre Baillargeon)
Date: Tue, 17 Sep 2002 23:08:41 +0000 (UTC)
Raw View
Allan_W@my-dejanews.com (Allan W) wrote in message news:<23b84d65.0209161107.23bea618@posting.google.com>...
> > Garry Lancaster:
>
> ...

Frankly, I'm starting to find this thread pointless. All replies go
along the same lines: it's dangerous to throw from destructors, so
don't do anything in destructors. No one considers the alternative: if
it were not dangerous to throw from destructors we could do useful
things in destructors.

All suggestions always have to do with putting the header/footer pair
in a function with the body either a virtual function or templated.
They all fail to address the usability problems. The usability
problems are:

- Having to put code in a separate function/object/class.
- Not having access to local variables.
- Spreading the code in many places, making it harder to
read/understand.
- One-use code forced to be put in a reusable function/object/class.
- Harder to nest multiple levels of arbitrary headers/footers.

Some claim that the destructor will somehow corrupt the data, but it
can be avoided *easily* with a single if. I don't only because I don't
need that capability.

The need is simple: have an automated, reusable and usable way to have
a pair of functions called around a piece of code.

As far as claim to abuse of the RAII name, I agree, but the concepts
are related: RAII is a specialized subset of the other, only taking
resources into consideration.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Allan_W@my-dejanews.com (Allan W)
Date: Tue, 17 Sep 2002 23:10:13 +0000 (UTC)
Raw View
> Allan W wrote:
> > Because a tool can be abused, doesn't make it a bad tool -- in fact,
> > any tool can be abused. (Do you know how dangerous a for()-loop is?
> > How about the "break" statement?)
> >
> > A footer is not a "Resource." The RAII (Resource Aquisition Is
> > Initialization) coding technique is not an appropriate way to generate it.

musiphil@bawi.org (Sungbom Kim) wrote
> But isn't it an idiom that is common and useful enough?
> Stroustrup also presented such a technique in his article
> "Wrapping C++ Member Function Calls"
> <http://www.research.att.com/~bs/wrapper.pdf>.

That paper presents a generalized technique for function prefix
and suffix operations. Note that he does acknowledge this technique
to be related to RAII. Stroustrup doesn't say exactly when this
technique is appropriate and when it is not, but note his examples.
   * In (1), he mentions a lock/unlock mechanism
   * The very end of (7) also uses lock/unlock as an example
   * Lock/unlock is used in (8), along with "Tracer"
   * In (11), "Tracer" is shown to "trace accesses to shared data"
Note that in both example uses, the second part is never optional --
if code throws while a resource is locked, it is normally vital to
unlock that resource, and if code throws while accessing shared
data, we normally still want to trace the fact that we're finished.

Consider for a moment this minor change from classic RAII:
    struct fileraii {
        fileraii(FILE*file) : f(file), needclose(true) {}
        void closed() { needclose=false; }
        ~fileraii() { if (needclose) close(f); }
    private:
        FILE *f;
        bool needclose;
    }

    int foo() {
        FILE *f = fopen("file", "r");
        fileraii closemyfile(f);
        // ... Operations which could throw

        // About to close the file
        closemyfile.closed(); //
        close(f);

        // More operations that could throw
    }

Here, if the function exits before the file is closed, the fileraii
object will close it automatically. However, we can supress this by
calling fileraii::closed(); we do this when we manually close the
file before we are ready to exit the function.

Conceptually, you might want exactly the opposite with DocumentSection --
you always want to write the footer, unless you never reach the end
of normal processing, in which case you want to supress it. A first
stab looks like this:

    struct DocumentSection {
        DocumentSection() : normalexit(false) { do_header(); }
        void ExitedNormally() { normalexit = true; }
        ~DocumentSection() { if (normalexit) do_footer(); }
    private:
        bool normalexit;
    }
    void foo() {
        DocumentSection d;
        do_body();
        d.ExitedNormally();
    }
Now if do_body throws, we never execute ExitedNormally, and therefore
never call do_footer. But this is just a complicated way to write:

    void foo() {
        do_header();
        do_body();
        do_footer();
    }

Once again, if do_body throws, do_footer is never called. Furthermore,
we haven't misused RAII -- there is no resource that needs to be
cleaned up, so RAII is inappropriate.

Yes -- RAII is common and useful. But that doesn't mean it can't be
misused. A report footer is still not a resource.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: pierrebai@hotmail.com (Pierre Baillargeon)
Date: Thu, 12 Sep 2002 19:40:45 +0000 (UTC)
Raw View
I think this thread is beginning to drift toward discussion of my
usage vs std related stuff. It may still be relevant as an example of
use...

glancaster@ntlworld.com ("Garry Lancaster") wrote in message news:<TbHf9.859$Pa7.63089@newsfep1-gui.server.ntli.net>...
>
> void foo() {
>     DocumentSection d; // ctor does header, dtor footer.
>     write_body();    // throws!
> }
>
> Even if the destructor succeeds, the footer will
> likely appear in the wrong place since the whole
> of the body may not have been written before the
> exception was thrown. If the destructor fails, the
> program will, by default, terminate. Mucking
> around with uncaught_exception or similar may
> change the exact behaviour, but it will still be a
> hard-to-understand mess.

In the particular example you give, the footer will be at the right
place, but the body content will be bad. But the current design is
that the PDF document must be all valid: an error output in a single
object makes the document invalid and it gets deleted.

But the fact is that the design also support erasing invalid PDF
file-objects. That capability is there because some usage of the PDF
framework sometimes realize in the middle of an object that the object
is not needed after all. This capability could be used in destructors
to remove invalid file-objects by detecting that an exception is in
flight. I have no use for this now though.

>
> There are other ways to automatically bracket
> a pre- and post- operation that don't suffer from
> these issues. For example, the Template Method
> pattern from Gamma et al.'s "Design Patterns",
> which can be implemented using virtual functions
> (as in the book) or C++ templates (shown below).
>
> void foo() {
>       DocumentSection d;    // Ctor and dtor now simple.
>       d( write_body );
> }
>
> template <typename BodyWriter>
> void DocumentSection::operator()(BodyWriter f) {
>     write_header();
>     f();
>     write_footer();
> }
>

Yes, I know of this pattern and it actually used in the RAII classes
to write the header and footer! The basic design is:

struct Writable
{
   virtual void begin_write(...) = 0;
   virtual void write(...) = 0;
   virtual void end_write(...) = 0;
};

struct Writer
{
   Writer( Writable & aWr ) : wr ( aWr )
   {
      wr.begin_write();
   }

   ~Writer()
   {
      wr.end_write();
   }
};

The problem with the your implementation is that the body, f() in your
code, has to be written in a derived classes. This disperses the code,
making it unreadable. It also requires writing a function for every
usage, making it hard to use. Also consider the requirement that
multiple levels of imbrication must be supported. Here's a typical
fictional example (include and namespace omited):

int main()
{
   PDF pdf("hello.pdf");
   Page page(pdf);
   Stream content(pdf);
   page[Name::Contents] = Ref<Stream>(content);
   FlateEncoder flate(content);
   ASCII85Encoder a85(flate)
   Writer writer1(a85);
   writer << "(Hello World!) Tj\n";
   Writer writer2(page);
}

Note that your suggestion would not suffer from usability problem if
it were possible to write local class that have access to the local
variable of the enclosing function. The code would still be less
readable IMO.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: glancaster@ntlworld.com ("Garry Lancaster")
Date: Fri, 13 Sep 2002 20:10:18 +0000 (UTC)
Raw View
Garry Lancaster:
> > void foo() {
> >     DocumentSection d; // ctor does header, dtor footer.
> >     write_body();    // throws!
> > }
> >
> > Even if the destructor succeeds, the footer will
> > likely appear in the wrong place since the whole
> > of the body may not have been written before the
> > exception was thrown. If the destructor fails, the
> > program will, by default, terminate. Mucking
> > around with uncaught_exception or similar may
> > change the exact behaviour, but it will still be a
> > hard-to-understand mess.

Pierre Baillargeon:
> In the particular example you give, the footer will be at the right
> place, but the body content will be bad.

;-)

That's another way of looking at the same behaviour.
Put it this way: if the exception occurs before the body
is fully written, the footer will appear before the end of the
body. (The end of the body will not be written at all.)
Agreed?

> But the current design is
> that the PDF document must be all valid: an error output in a single
> object makes the document invalid and it gets deleted.

Seems reasonable, although I still think it would be
more intuitive for all document output to stop as soon
as the first error is encountered. The main problem is
still, as we know, how to deal with destructors that
throw.

[snip]

> > There are other ways to automatically bracket
> > a pre- and post- operation that don't suffer from
> > these issues. For example, the Template Method
> > pattern from Gamma et al.'s "Design Patterns",
> > which can be implemented using virtual functions
> > (as in the book) or C++ templates (shown below).
> >
> > void foo() {
> >       DocumentSection d;    // Ctor and dtor now simple.
> >       d( write_body );
> > }
> >
> > template <typename BodyWriter>
> > void DocumentSection::operator()(BodyWriter f) {
> >     write_header();
> >     f();
> >     write_footer();
> > }
> >

> Yes, I know of this pattern and it actually used in the RAII classes
> to write the header and footer!

As I explained before, unless the destructor
is releasing a resource, it isn't using RAII.
Writing a footer is not releasing a resource.
A destructor whose only action is to write a
footer is not using RAII.

RAII does not mean "putting code into destructors".
It's more specific than that.

> The basic design is:
>
> struct Writable
> {
>    virtual void begin_write(...) = 0;
>    virtual void write(...) = 0;
>    virtual void end_write(...) = 0;
> };
>
> struct Writer
> {
>    Writer( Writable & aWr ) : wr ( aWr )
>    {
>       wr.begin_write();
>    }
>
>    ~Writer()
>    {
>       wr.end_write();
>    }
> };
>
> The problem with the your implementation is that the body, f() in your
> code, has to be written in a derived classes.

Not true. f is either a pointer to a free function or a
function object ("functor"). It's the same technique used
by many of the standard <algorithm>s e.g. std::for_each.

> This disperses the code, making it unreadable.

It "encourages" functional decomposition. Which,
like most good things, can be overdone. Now you've
supplied an example we can see this could be an
issue in this case.

> It also requires writing a function for every usage,
> making it hard to use.

Since it's a template, this isn't true. We supply a
function template and the *compiler* writes the
correct function for each instantiation.

> Also consider the requirement that
> multiple levels of imbrication must be supported.

What do you mean by "imbrication"? (I'm not being
pedantic about a typo or spelling - I really don't know
what you mean.)

> Here's a typical
> fictional example (include and namespace omited):
>
> int main()
> {
>    PDF pdf("hello.pdf");
>    Page page(pdf);
>    Stream content(pdf);
>    page[Name::Contents] = Ref<Stream>(content);
>    FlateEncoder flate(content);
>    ASCII85Encoder a85(flate)
>    Writer writer1(a85);
>    writer << "(Hello World!) Tj\n";
>    Writer writer2(page);
    // Dtors are now called in the following order:
    // writer2
    // writer1
    // a85
    // flate
    // content
    // page
    // pdf
    // Is that the intended order?
> }

Good. Examples make things so much easier to
explain.

Can you:

(a) Indicate which of these objects has a destructor
that can throw (these are the ones that need
attention); and

(b) Show the body of one of these destructors (so
we can see what the current behaviour is in the
face of exceptions)

?

Kind regards

Garry Lancaster
Codemill Ltd
Visit our web site at http://www.codemill.net

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: glancaster@ntlworld.com ("Garry Lancaster")
Date: Wed, 11 Sep 2002 18:32:36 +0000 (UTC)
Raw View
> > Pierre Baillargeon:
> > > I'd agree if RAII had not made me so much more productive. I could
> > > write a long rant detailing my experience, but I'll simply say this:
> > > these posts made me reflect on my usage of RAII and I realized that
> > > for an entire year I completely forgot that there could be bugs in the
> > > base layer of my design because it has been so flawless, thanks to
> > > RAII. Unfortunately, it means my destructors can throw.

Garry Lancaster:
> > Aha. This is the root of all the confusion, I think. RAII
> > does not mean destructors must be able to throw.
> >
> > If you are using your throwing destructors with the
> > standard libary or any other template library you
> > really need to address this misconception. See
> > for example this part of the C++ standard:

Pierre Baillargeon:
> I am not, so the point is not relevant.

I still think any misconception worth squashing.

> To give you an idea of the
> kind of work I do using RAII, I use it to output the structure of a
> PDF file. The PDF specification requires a lot of meta information to
> be kept around and output later, and all data is embedded in objects
> with different layers of embedding, compression and encoding. Without
> RAII, the user must remember when to call each little function.
> Everything has a header and footer, most of the time multi-levels. It
> is easy to get wrong and the PDF tools are not friendly as far as
> error reporting goes.

RAII stands for "resource allocation is initialization".
This is still rather opaque. Actually, it is the technique
of using a destructor to release resources allocated
in a constructor.

Using destructors to create document footers or
add other document formatting is rather stretching
the definition of "resource". Using destructors for
other things is not necessarily wrong, but it isn't RAII.
(For example, simple sentry classes can be very
useful.)

Having said that, destructors that throw are often
problematic. If we have a class that formats a
document header in its constructor and formats
a document footer in its destructor, where the
latter may throw, will it really do what we want
should an exception be thrown after the object's
construction and prior to destruction?

void foo() {
    DocumentSection d; // ctor does header, dtor footer.
    write_body();    // throws!
}

Even if the destructor succeeds, the footer will
likely appear in the wrong place since the whole
of the body may not have been written before the
exception was thrown. If the destructor fails, the
program will, by default, terminate. Mucking
around with uncaught_exception or similar may
change the exact behaviour, but it will still be a
hard-to-understand mess.

There are other ways to automatically bracket
a pre- and post- operation that don't suffer from
these issues. For example, the Template Method
pattern from Gamma et al.'s "Design Patterns",
which can be implemented using virtual functions
(as in the book) or C++ templates (shown below).

void foo() {
      DocumentSection d;    // Ctor and dtor now simple.
      d( write_body );
}

template <typename BodyWriter>
void DocumentSection::operator()(BodyWriter f) {
    write_header();
    f();
    write_footer();
}

> I can't possibly show you proprietary code.

And we don't want to see it either! Just create
an example that demonstrates the issue and is
as short and simple as possible. At the moment
you're forcing us to guess exactly what your real
problem is.

[snip]

Kind regards

Garry Lancaster
Codemill Ltd
Visit our web site at http://www.codemill.net

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Allan_W@my-dejanews.com (Allan W)
Date: Mon, 16 Sep 2002 19:14:01 +0000 (UTC)
Raw View
> Garry Lancaster:
> > > void foo() {
> > >     DocumentSection d; // ctor does header, dtor footer.
> > >     write_body();    // throws!
> > > }
> > >
> > > Even if the destructor succeeds, the footer will
> > > likely appear in the wrong place since the whole
> > > of the body may not have been written before the
> > > exception was thrown. If the destructor fails, the
> > > program will, by default, terminate. Mucking
> > > around with uncaught_exception or similar may
> > > change the exact behaviour, but it will still be a
> > > hard-to-understand mess.
>
> Pierre Baillargeon:
> > In the particular example you give, the footer will be at the right
> > place, but the body content will be bad.
>
glancaster@ntlworld.com ("Garry Lancaster") wrote
> ;-)
>
> That's another way of looking at the same behaviour.
> Put it this way: if the exception occurs before the body
> is fully written, the footer will appear before the end of the
> body. (The end of the body will not be written at all.)
> Agreed?

So you're suggestion would be to make the code snippet above become
good style?

For the reasons that you first mentioned, using the destructor of
object DocumentSection to write the report footer would be a bad
idea. The obvious refactoring is also probably the best:

    void foo() {
        // Ctor initializes, but does not write anything to report
        DocumentSection d;
        d.write_header();  // Writes header
        write_body();      // Throws!
        d.write_footer();  // Writes footer
        // Dtor cleans up resources, but does not write to report
    }

> > But the current design is
> > that the PDF document must be all valid: an error output in a single
> > object makes the document invalid and it gets deleted.
>
> Seems reasonable, although I still think it would be
> more intuitive for all document output to stop as soon
> as the first error is encountered. The main problem is
> still, as we know, how to deal with destructors that
> throw.

Exactly, which is why RAII-style footers are a bad idea.

Because a tool can be abused, doesn't make it a bad tool -- in fact,
any tool can be abused. (Do you know how dangerous a for()-loop is?
How about the "break" statement?)

A footer is not a "Resource." The RAII (Resource Aquisition Is
Initialization) coding technique is not an appropriate way to generate it.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: glancaster@ntlworld.com ("Garry Lancaster")
Date: Tue, 10 Sep 2002 20:27:46 +0000 (UTC)
Raw View
I should have written:

> If you have a particular cleanup sequence you are
> having trouble implementing *with* a no-throw
> destructor, you could post it here [actually, probably
> more appropriate on comp.lang.c++.moderated],
> although the solution will likely be along the lines
> already discussed.

Sorry about that.

- Garry Lancaster.


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: pierrebai@hotmail.com (Pierre Baillargeon)
Date: Wed, 11 Sep 2002 02:55:51 +0000 (UTC)
Raw View
glancaster@ntlworld.com ("Garry Lancaster") wrote in message news:<ar_e9.1193$Yc7.36404@newsfep2-gui>...
>
> Pierre Baillargeon:
> > I'd agree if RAII had not made me so much more productive. I could
> > write a long rant detailing my experience, but I'll simply say this:
> > these posts made me reflect on my usage of RAII and I realized that
> > for an entire year I completely forgot that there could be bugs in the
> > base layer of my design because it has been so flawless, thanks to
> > RAII. Unfortunately, it means my destructors can throw.
>
> If you are using your throwing destructors with the
> standard libary or any other template library you
> really need to address this misconception. See
> for example this part of the C++ standard:

I am not, so the point is not relevant. To give you an idea of the
kind of work I do using RAII, I use it to output the structure of a
PDF file. The PDF specification requires a lot of meta information to
be kept around and output later, and all data is embedded in objects
with different layers of embedding, compression and encoding. Without
RAII, the user must remember when to call each little function.
Everything has a header and footer, most of the time multi-levels. It
is easy to get wrong and the PDF tools are not friendly as far as
error reporting goes.

I can't possibly show you proprietary code. I believe I already know
the possible alternative. I also know from experience that anything
that can be automated must be, it's the only way to reduce the error
rate while improving productivity.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: glancaster@ntlworld.com ("Garry Lancaster")
Date: Mon, 9 Sep 2002 19:03:44 +0000 (UTC)
Raw View
Garry Lancaster:
> > Good general advice is to do as the standard library
> > does and not to throw from a destructor. Clean-up
> > which really can fail is done in a non-destructor
> > function.

Pierre Baillargeon:
> I'd agree if RAII had not made me so much more productive. I could
> write a long rant detailing my experience, but I'll simply say this:
> these posts made me reflect on my usage of RAII and I realized that
> for an entire year I completely forgot that there could be bugs in the
> base layer of my design because it has been so flawless, thanks to
> RAII. Unfortunately, it means my destructors can throw.

Aha. This is the root of all the confusion, I think. RAII
does not mean destructors must be able to throw.

If you are using your throwing destructors with the
standard libary or any other template library you
really need to address this misconception. See
for example this part of the C++ standard:

"[lib.res.on.functions] 17.4.3.6 Other functions

1 In certain cases (replacement functions, handler functions,
operations on types used to instantiate standard library
template components), the C + + Standard Library depends
on components supplied by a C + + program. If these
components do not meet their requirements, the Standard
places no requirements on the implementation.

2 In particular, the effects are undefined in the following
cases:
....
- if any replacement function or handler function or
destructor operation throws an exception, unless
specifically allowed in the applicable Required behavior
paragraph."

If you have a particular cleanup sequence you are
having trouble implementing without a no-throw
destructor, you could post it here, although the
solution will likely be along the lines already
discussed.

Kind regards

Garry Lancaster
Codemill Ltd
Visit our web site at http://www.codemill.net

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: pierrebai@hotmail.com (Pierre Baillargeon)
Date: Thu, 5 Sep 2002 18:41:07 +0000 (UTC)
Raw View
glancaster@ntlworld.com ("Garry Lancaster") wrote in message news:<tHkd9.24180$5g6.553497@newsfep2-win.server.ntli.net>...
>
> Without knowledge of its call context over and above
> whether stack unwinding is occurring (even if it
> knows whether it was occurring during object
> construction), the foo destructor cannot know
> whether it is safe to throw. Any solution that
> seeks to make the throwing of exceptions from
> destructors context-dependent needs to address
> this point.

I agree with your point but disagree with your analysis. The fact that
a destructor throws must be documented and using objects that have a
throwing destructor implies (currently) responsabilities on the
programmer to use it correctly. All this supports the need for an
easier solution!

>
> > There
> > is also the problem of classes that I do not write, like standard
> > containers, althought I haven't encoutered problems so far in real
> > life.
>
> See 17.4.4.8/3:
>
> "No destructor operation defined in the C++ Standard
> Library will throw an exception."

Since they are templated, they only asserts this for their own code.
There may be a requirement that destructors of contained objects not
throw. I only used them as an example, as I've never had any problem.

>
> > The solution I proposed is just like yours, except it involves no
> > user-written code.
>
> Your previous explanation of set_unwinding_resumption
> mentioned setting flags in destructors. The
> std::uncaught_exception approach also is invasive of
> the destructor. So there is no difference in terms of
> whose code must be changed.

You are right, because my original post was trying to be conservative
in its usage of set_unwinding_resumption(). In reality, I rather think
that I would set the flag as the first thing in main() and design all
classes accordingly. That is what I'm doing now, except that I have to
salt all my code with exception filtering.

> Good general advice is to do as the standard library
> does and not to throw from a destructor. Clean-up
> which really can fail is done in a non-destructor
> function.

I'd agree if RAII had not made me so much more productive. I could
write a long rant detailing my experience, but I'll simply say this:
these posts made me reflect on my usage of RAII and I realized that
for an entire year I completely forgot that there could be bugs in the
base layer of my design because it has been so flawless, thanks to
RAII. Unfortunately, it means my destructors can throw.

> If we absolutely must do the might-throw
> cleanup in a destructor we have two reasonable
> choices if an exception is thrown:
>
> (a) If the program can survive the failure and
> continue to work correctly. Swallow the exception
> inside the destructor, maybe emit some kind of
> run-time diagnostic then continue normal exectution.
>
> (b) If the program cannot survive the failure.
> Terminate.
>
> The third choice - to make the behaviour of the
> destructor dependent on its context - is in my
> experience very rarely required, if ever. As such,
> providing the language with another system for
> doing it, over and above what we already have,
> seems to me a low priority. If your experience is
> different, perhaps you could show one or two
> motivating examples.

This is were the biggest disagreement lies, IMO. The only context is
weither stack-unwinding is under way and as such that there is already
a problem being reported. The idea is to allow error-reporting from
destructor unless there is already an error in the midst of being
reported.

IOW, both (a) and (b) are wrong. Swallowing all errors even when they
could have been reported is wrong. Letting the program terminate just
because a second error occured while a first error is in flight to the
higher-level logic in charge of error handling is wrong.

All non-exception based error handling I've used always return a
single error. They either return the earliest or latest error. Some
more intelligent error handling systems will log the unreported error.
That is what I want when using exceptions.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: pierrebai@hotmail.com (Pierre Baillargeon)
Date: Tue, 3 Sep 2002 17:49:20 +0000 (UTC)
Raw View
"Garry Lancaster" <glancaster@ntlworld.com> wrote in message news:<zllb9.458$S51.25977@newsfep1-gui.server.ntli.net>...
>
> (However, item 19 in Herb Sutter's "More Exceptional
> C++" explains why this can sometimes generate false
> positives. If you don't have the book the problem is
> shown by:
>
> bar::~bar() {
>     try {
>         foo f;
>         // whatever.
>     } catch(...) {
>         // whatever.
>         if( !std::uncaught_exception() ) throw;
>     }
> }
>
> If a bar is destroyed during stack unwinding, f's
> destructor will never throw, even though it is safe
> to do so.)
>
> I don't think the proposal adds enough to what we
> already have to warrant its inclusion in the language.
>

Doesn't your example shows that the situation is not simple? I know
the solution is to compare the situation as seen in the constructor
with the one in the destructor, but it involves a burden on all
classes. Each data member also has to keep it own private value. There
is also the problem of classes that I do not write, like standard
containers, althought I haven't encoutered problems so far in real
life.

The solution I proposed is just like yours, except it involves no
user-written code. It simplifies the language IMO: the destructors
cease to be special functions in respect to exceptions, as far as the
code writer is concerned. One less error for beginner and intermediate
programmers.

The current shortest solution is to defer all destructor code to
another function and use a function template taking an object and a
member-function pointer to do the try-catch-test-rethrow. The problem
here is that the function must be private to avoid bad calls and thus
the template with all its yucky syntax must be made friend.

Finally, the current code has a big "huh?" factor with newcomers, and
it easily forgotten in new classes.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: terekhov@web.de (Alexander Terekhov)
Date: Tue, 3 Sep 2002 20:42:14 +0000 (UTC)
Raw View
Garry Lancaster wrote:
[...]
> I don't think the proposal adds enough to what we
> already have to warrant its inclusion in the language.

What about "bool future_std::expected_exception<T>" and
the entire "10 o'clock"-wish list:

http://groups.google.com/groups?q=insubject:overloaded+insubject:destructors+author:terekhov%40web.de&scoring=d&filter=0
(Subject: Re: A half serious proposal: overloaded destructors)

<?>

Oh, BTW, I've got one more "item"...

< Forward Inline; FYI: http://www.gotw.ca/publications/xc++-errata.htm >

-------- Original Message --------
Message-ID: <3D74BF08.AA0D377@web.de>
Date: Tue, 03 Sep 2002 15:54:16 +0200
From: Alexander Terekhov <terekhov@web.de>
Reply-To: terekhov@web.de
Newsgroups: comp.lang.c++.moderated
Subject: Re: "Exceptional C++", Item 5 (GoTW 16b, I think)


Jim Fischer wrote:
>
> At the top of page 16 in Herb Sutter's book "Exceptional C++" he
> mentions that a fixed_vector object will be in an inconsistent state if
> one of the T assignments fails during the copy() operation. The text and
> code on pp 16-18 takes some steps to address this issue. IMO, however,
> the proposed "new-and-improved" code at the bottom of page 16 has
> introduced a new exception-safety issue. Note that the templated
> converting ctor and the copy ctor (p.16) both have the following form:
>
> fixed_vector( const fixed_vector<T,size>& other )
> : v_( new T[size] )
> {
>     copy( other.begin(), other.end()+min(size,osize), begin() );
> }
>
> If the copy() call fails due to an exception being thrown, these ctors
> will "leak" their dynamically allocated memory, right?

Right, unless you prefer to use REAL objects vs "dumb" pointers;
auto_ptr_array<T>, in this case for "v_" member. Well, another
RAII-less-approach that wouldn't "steal" each and every exception
(as catch(...)/rethrow without ES protection unfortunately does),
would be to use some currently nonexistent "soft"/"cleanup"-catch
"construct":

fixed_vector( const fixed_vector<T,size>& other )
: v_( new T[size] )
{
    try {
      copy( other.begin(), other.end()+min(size,osize), begin() );
    }
    catch(...) CLEANUP { /* THIS DOESN'T CATCH *UNEXPECTED* EXCEPTIONS */
                         /* THIS DOES RETHROW EXCEPTIONS AUTOMATICALLY */
                         /* NOTHING ELSE CAN BE THROWN FROM THIS BLOCK */
      delete[] v_;
    }
}

http://groups.google.com/groups?threadm=Aiob9.18%24c7.126076%40news.cpqcorp.net
(check out followup(s) as well; and, BTW, this
 entire thread is about exceptions and cleanup)

regards,
alexander.

--
"~sentry();

 4 If ((os.flags() & ios base::unitbuf) && !uncaught exception())
   is true, calls os.flush()."   --ISO/IEC 14882:1998(E) Pg. 642

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: glancaster@ntlworld.com ("Garry Lancaster")
Date: Wed, 4 Sep 2002 18:00:18 +0000 (UTC)
Raw View
Garry Lancaster:
> > (However, item 19 in Herb Sutter's "More Exceptional
> > C++" explains why this can sometimes generate false
> > positives. If you don't have the book the problem is
> > shown by:
> >
> > bar::~bar() {
> >     try {
> >         foo f;
> >         // whatever.
> >     } catch(...) {
> >         // whatever.
> >         if( !std::uncaught_exception() ) throw;
> >     }
> > }
> >
> > If a bar is destroyed during stack unwinding, f's
> > destructor will never throw, even though it is safe
> > to do so.)
> >
> > I don't think the proposal adds enough to what we
> > already have to warrant its inclusion in the language.

Pierre Baillargeon:
> Doesn't your example shows that the situation is not simple? I know
> the solution is to compare the situation as seen in the constructor
> with the one in the destructor, but it involves a burden on all
> classes. Each data member also has to keep it own private value.

That won't work e.g.

bar::~bar() {
  foo f;
}

As before, a bar is destroyed during stack unwinding
but in this case it is *not* safe for f's destructor to throw.

Without knowledge of its call context over and above
whether stack unwinding is occurring (even if it
knows whether it was occurring during object
construction), the foo destructor cannot know
whether it is safe to throw. Any solution that
seeks to make the throwing of exceptions from
destructors context-dependent needs to address
this point.

> There
> is also the problem of classes that I do not write, like standard
> containers, althought I haven't encoutered problems so far in real
> life.

See 17.4.4.8/3:

"No destructor operation defined in the C++ Standard
Library will throw an exception."

> The solution I proposed is just like yours, except it involves no
> user-written code.

Your previous explanation of set_unwinding_resumption
mentioned setting flags in destructors. The
std::uncaught_exception approach also is invasive of
the destructor. So there is no difference in terms of
whose code must be changed.

Good general advice is to do as the standard library
does and not to throw from a destructor. Clean-up
which really can fail is done in a non-destructor
function. If we absolutely must do the might-throw
cleanup in a destructor we have two reasonable
choices if an exception is thrown:

(a) If the program can survive the failure and
continue to work correctly. Swallow the exception
inside the destructor, maybe emit some kind of
run-time diagnostic then continue normal exectution.

(b) If the program cannot survive the failure.
Terminate.

The third choice - to make the behaviour of the
destructor dependent on its context - is in my
experience very rarely required, if ever. As such,
providing the language with another system for
doing it, over and above what we already have,
seems to me a low priority. If your experience is
different, perhaps you could show one or two
motivating examples.

[snip]

Kind regards

Garry Lancaster
Codemill Ltd
Visit our web site at http://www.codemill.net

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "Garry Lancaster" <glancaster@ntlworld.com>
Date: 31 Aug 2002 03:55:01 GMT
Raw View
Pierre Baillargeon:
> When a non-catched exception is thrown in the destructor of an object
> during stack unwinding, the current standard requires the following
> behavior. The C++ run-time somehow checks if an exception handler can
> be found to catch the exception upto the destructor call made by the
> stack-unwinding. If one is found, the exception is catched as always.
> If none is found (i.e. the destuctor exit via an excption),
> terminate() is called.
>
> What I propose is that this last behavior be selectable. A function,
> with the signature bool set_unwinding_resumption(bool), would allow
> toggling between the current behavior and a new one: that the
> unhandled exception that exited the destructor be thrown-away (no pun
> intended) and that stack unwinding resume as if teh destructor had
> exited normally. It would return the previous setting.

You can already get close to the behaviour you want
by swallowing exceptions in your destructors if
std::uncaught_exception() is true e.g.

foo::~foo() {
    try {
        // Code that might throw...
    } catch(...) {
        if( !std::uncaught_exception() ) throw;
    }
}

(However, item 19 in Herb Sutter's "More Exceptional
C++" explains why this can sometimes generate false
positives. If you don't have the book the problem is
shown by:

bar::~bar() {
    try {
        foo f;
        // whatever.
    } catch(...) {
        // whatever.
        if( !std::uncaught_exception() ) throw;
    }
}

If a bar is destroyed during stack unwinding, f's
destructor will never throw, even though it is safe
to do so.)

I don't think the proposal adds enough to what we
already have to warrant its inclusion in the language.

Kind regards

Garry Lancaster
Codemill Ltd
Visit our web site at http://www.codemill.net

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: pierrebai@hotmail.com (Pierre Baillargeon)
Date: Mon, 26 Aug 2002 18:31:54 GMT
Raw View
When a non-catched exception is thrown in the destructor of an object
during stack unwinding, the current standard requires the following
behavior. The C++ run-time somehow checks if an exception handler can
be found to catch the exception upto the destructor call made by the
stack-unwinding. If one is found, the exception is catched as always.
If none is found (i.e. the destuctor exit via an excption),
terminate() is called.

What I propose is that this last behavior be selectable. A function,
with the signature bool set_unwinding_resumption(bool), would allow
toggling between the current behavior and a new one: that the
unhandled exception that exited the destructor be thrown-away (no pun
intended) and that stack unwinding resume as if teh destructor had
exited normally. It would return the previous setting.

I currently use RAII design extensively in my code. One limitation is
the problem of cleanup code that can fail and the problems associated
with throwing from a destructor. The proposed solution would solve my
problem. The way it would be used is that my RAII classes would set a
flag in their destructor (using RAII!) that would tell my exception
classes to call set_unwinding_resumption(true) in  their constructor
and set_unwinding_resumption(previous_setting) in their destructor.

(Yes, I know that loosing an exception may not be appropriate in all
designs and situations, but it is my designs as I use the exception
mechanism exclusively to transfer program execution to the appropriate
code-level. It is not used to choose specialized recovery method in
the different catch blocks.)

As far as I can tell, due to the requirements of the current standard,
conforming implementation already do all the work required by this
proposal, except that a test would need to be added before calling
terminate(). That is they must already:

- Walk up the stack in the offending destructor.
- Be able to tell that they have walked upto the destructor caller.
- Stop scanning there (i.e.: at the exact program location of the
call) and call terminate(). This is where the test would be and
resumption would take place.

The effect of the proposal on existing code is nul since the current
bahavior would remain since no call to std::set_unwinding_resumption()
can exist today. The only possible impact would be if a function with
the same name existed in the global namespace and the std namespace
were in use. The length and semantic of the proposed function name
makes this extremely unlikely. A different name with a leading
underscore would reduce the likelihood even further.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]