🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.std.c++
Article #3982

Re: sample auto_ptr template

#3982
From: J. Kanze
Date: Wed, 10 Apr 1996 16:56
269 lines
13146 bytes
In article <4kcr2d$p03@jabba.lehman.com> ajay@lehman.com (Ajay Kamdar)
writes:

|> In article <bill-0804960932250001@bgibbons.vip.best.com>,
|> Bill Gibbons <bill@gibbons.org> wrote:
|> >In article <4k4noe$igl@jabba.lehman.com>, ajay@lehman.com (Ajay Kamdar) wrote:
|> >
|>            [ snip ]
|> >> 
|> >> What's wrong with this? It doesn't require copy semantics
|> >> for auto_ptr. Yet both the caller and the callee
|> >> are exception safe and there is no loss of clarity.
|> >
|> >The problem is that it requires handling the raw pointer.
|> >Any time you transfer ownership by using release() to extract
|> >the raw pointer and then later use the raw pointer to
|> >construct another auto_ptr, there is a window where there is
|> >no exception safety.
|> >

|> You didn't say whether the example I gave was exception
|> safe or not.

But I did.  It's not.

|> To reiterate the main points of the example
|>    + The caller directly initializes an auto_ptr with
|>      the returned value from the calee.
|> 	 auto_ptr<X> ptr(get_X());

|>    + The callee releases the pointer from it's own
|>      auto_ptr in it's return statement.

|> No copy constructors are involved in the return. Hence
|> there is no possibility of an exception thrown from
|> a user defined copy constructor. So which window are
|> you referring to during which there is no exception
|> safety? I claim there is none.

The destructors of any local variables in get_X.

|> >Of course you can carefully craft the code to make sure that
|> >no exceptions can be propagated during the window.  But there
|> >are two problems:

|> The example I gave is straight forward C++ programming; no
|> contortions or careful coding are required. The coding
|> pattern is simple enough that even junior programmers can
|> be taught to follow it easily.

The only real solution I can think of to be 100% exception safe would be
to write get_X as follows:

	T*
	get_X()
	{
		auto_ptr< T >	localTmp ;
		{
			//  All of the work here, including assigning the actual
			//  pointer to localTmp, and above all, all local variables
			//  except localTmp here.
		}
		return localTmp.release() ;
	}

While the pattern isn't that complicated, it is certainly not what we
are used to, and is likely to be forgotten from time to time.

|> Many other more useful extensions have been voted down by
|> the commitee on the grounds that the requested extension
|> could be implemented by existing mechanisms within the
|> language, even if those techniques required a fair
|> amount of work for the programmer. That stance cannot be
|> more applicable than in the case of the copy semantics
|> of auto_ptr -- there is simply no need for such semantics
|> because identical exception safety can be achieved by
|> a simple coding pattern.

I imagine that the committee is more tolerant with regards to extensions
which are purely library issues.  Library issues are almost guaranteed
by their very nature not to have a surprise side effect in other parts
of the language.

|> >  (1) The maintainers of the code may not be as careful about
|> >      exception safety.  When everything is handled by
|> >      auto_ptr the risk of bugs creeping in is smaller.  Such
|> >      bugs are very difficult to find by testing.

|> Let's analyze which code is more difficult to maintain
|> and difficult to debug -- one using the copy semantics
|> of auto_ptr or one wihout copy semantics of auto_ptr.

|>   With copy semantics
|>   ===================
|>      If the copy ctor of auto_ptr taking a const auto_ptr&
|>      releases its raw pointer by casting away const,
|>      a programmer can accidentaly "lose" the raw pointer from
|>      within an auto_ptr -- even if the programmer was working
|>      with a const auto_ptr. This occurrs because auto_ptr
|>      violates const correctness and releases the raw pointer
|>      from the raw object.

|>      If the copy ctor of the auto_ptr taking a const auto_ptr&
|>      takes ownership of the raw pointer by casting away const
|>      and setting a flag (the latest proposal), the programmer
|>      can end up with a dangling pointer in the original
|>      auto_ptr if the new auto_ptr destructs first and deletes
|>      the raw pointer.

|>      It is possible that even extensive testing may not
|>      uncover such a problem on a less frequently traversed
|>      path in the code. Even if the path is traversed during
|>      testing, an error may or may not be generated when using
|>      the dangling pointer. Hence it is very possible that the
|>      error might escape it to production code.

I think the question here is one of patterns.  The auto_ptr is designed
to implement simply certain basic, frequently occuring patterns.  If it
is used for other things, it is unsafe.

Because its only legitimate use *is* these frequently occuring patterns,
any other use is easily detected by code review.  The alternative, using
raw pointers, is more dangerous, because raw pointers have so many uses;
the error is not immediately apparent.

|>   Without copy semantics of auto_ptr
|>   ==================================
|>     By eliminating any surprises due to the standard
|>     auto_ptr violating const correctness and leaving
|>     behind dangling pointers, program correctness is
|>     actually *improved*.

|>     But for the moment assume that even the simple pattern
|>     of using auto_ptr (release() on return in callee,
|>     direct initialization of auto_ptr in caller) is
|>     not followed correctly in some situation. Two major
|>     scenarios are possible:
|>        1. The error happens on a frequently travesed path
|> 	  in the code, resulting in frequent ommission to
|> 	  call delete.

|> 	  In such situations, any of the many good leak
|> 	  detectors on the market will show the error
|> 	  during testing. The error will be fixed
|> 	  before it reached production.

|>       2. The error is rare enough that it occurs only in
|> 	 production.

|> 	 By definition, the error is rare. So the application
|> 	 will lose some resource each time. In practice,
|> 	 each occurence of a failure to delete a resource is
|> 	 not disastorous on its own, and the application will
|> 	 go on.


|> So which one is better for the C++ programmer? An approach
|> which can lead to disastorous surprises at run-time
|> (auto_ptr with copy semantics approach) definitely does
|> not look the right choice under any stretch of imagination.

In short, you are suggesting that memory leaks are OK, as long as they
don't occur too often.  This isn't the case for my applications.

|> >  (2) The interface of get_X does not implicitly document
|> >      that the returned pointer refers to an object which
|> >      should be automatically deleted on an exception.
|>                                ^^^^^^^^^^^^^^^^^^^^^^^

|> Now we are getting close to what I firmly believe is the
|> real driving reason behind the push for auto_ptr to
|> have copy semantics.

|> Note that in the above quoted text, the reference to
|> exceptions in "deleted on an exception" is irrelevant.
|> In this case, the caller must always delete the ptr
|> allocated by the callee() -- exception or no exception.
|> Hence the use of auto_ptr in this case has nothing to
|> do with exception safety. But it has everything to do
|> with trying to replace documentation accompanying
|> get_X() saying that the caller must delete the pointer.

No.  The caller cannot possibly delete the pointer, since the pointer is
never assigned at the caller side if an exception occurs.  The caller
never even sees the pointer.

The whole point of the copy semantics is that there is a period of time
after the evaluation of the return value in the return statement when
neither the callee nor the caller have access to the pointer.  Using a
temporary class object means, however, that this object does have
access, and can delete the object if an exception is thrown.  The
alternative is somehow to ensure that no exception can be thrown.  This
is not always trivial.

|> In isolation, it is a good goal to replace comments
|> which prescribe certain behavior with code constructs
|> which automatically take care of the issue. By
|> giving copy semantics to auto_ptr, the need to
|> document transfer of ownership of pointer is sourght
|> to be eliminated. I firmly believe that *this* is the
|> real reason behind the copy semantics of auto_ptr, with
|> exception safety only a peripheral issue at best. 
|> Unfortunately, it is abundantly clear that this attempt
|> at replacing documentation by giving auto_ptr copy
|> semantics has pretty high costs.

There are two separate problems: the unexpected semantics of assignment,
and the exception safety.  Because of the way the language is defined,
we can only define auto_ptr to handle one correctly; the other must be
left to comments.  (I think that we more or less agree up until this
point.)  The question is: which one?

IMHO, requiring the programmer to handle the exception safety is
non-intuitive, difficult, and error prone.  While I agree that requiring
the programmer to handle the strange copy semantics is non-intuitive, it
is relatively easy to check.  In all *valid* uses of auto_ptr, an
auto_ptr which is copied should immediately go out of scope.  This is
trivially true in the case of source functions, like get_X.  The only
point which requires a little bit of attention is when copying to a
sink; I would generally try and arrange my code so that the call to the
sinking function is the last thing in the block in which the auto_ptr
was declared, but I'm not certain that this is always possible or
natural.  (In practice, until now, it has always been the most natural
way of writing the code anyway.)

Any attempts to assign between two declared auto_ptr's, or to initialize
an auto_ptr with another declared auto_ptr, should be caught by code
review.

|> I strongly urge those who can do something about this
|> (those on the committee) to step back and separate
|> the wheat from the chaff. Don't make auto_ptr harder
|> to use for *all* C++ programmers by allowing it to
|> have copy semantics. Make a separate class
|> (taligent_ptr) which has the copy semantics for
|> those who *choose* to use it, rather than forcing
|> everyone to program with an unfortunate choice.

I believe that John Skaller had a proposal with something like six
different pointer types.  The committee found this a little too
complicated for its likings (I would agree), and settled on defining two
(or maybe three).

For various reasons, it was unable to find a definition of counted_ptr
which would obtain a concensus.  In some ways, this is regrettable, but
if you look at the implementations of this class in Barton and Nackman,
on one hand, and Meyers II on the other, you can see that in fact, there
are contradictory goals to be met.  I have no problem understanding why
no concensus could be reached.

In the case of auto_ptr, those (like Bill Gibbons) with extensive
practical experience actually using such a pointer indicated that to be
useful, it needed copy semantics.  (I might add that this corresponds
exactly to my experience.)  Without copy semantics, it simply wouldn't
be used in most cases.  (Or programmers would resort to tricks such as
returning the pointer as a raw pointer, with the resulting loss of
exception safety.)
-- 
James Kanze           (+33) 88 14 49 00          email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs Bourgeois, 67000 Strasbourg, France
Conseils en informatique industrielle --
                            -- Beratung in industrieller Datenverarbeitung
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your 
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu 
]
Technical Details
Message-ID: <KANZE.96Apr10111407@gabi.gabi-soft.fr>
Path: arc1-ca.pugleaf.net!fido.asd.sgi.com!austern
References: <009A04DA6A831C40.49800EAC@ittpub.nl> <bill-0504961003150001@bgibbons.vip.best.com> <4k4noe$igl@jabba.lehman.com> <bill-0804960932250001@bgibbons.vip.best.com> <4kcr2d$p03@jabba.lehman.com>

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!fido.asd.sgi.com!austern
From: kanze@gabi-soft.fr (J. Kanze)
Newsgroups: comp.std.c++
Subject: Re: sample auto_ptr template
Date: 10 Apr 1996 09:56:31 PDT
Organization: GABI Software, Sarl.
Approved: austern@isolde.mti.sgi.com
Message-ID: <KANZE.96Apr10111407@gabi.gabi-soft.fr>
References: <009A04DA6A831C40.49800EAC@ittpub.nl>
 <bill-0504961003150001@bgibbons.vip.best.com> <4k4noe$igl@jabba.lehman.com>
 <bill-0804960932250001@bgibbons.vip.best.com> <4kcr2d$p03@jabba.lehman.com>
NNTP-Posting-Host: isolde.mti.sgi.com
X-Original-Date: 10 Apr 1996 09:14:07 GMT
In-Reply-To: ajay@lehman.com's message of 09 Apr 1996 09:58:11 PDT
X-Auth: PGPMoose V1.1 PGP comp.std.c++
 iQBVAwUBMWvoQUy4NqrwXLNJAQENFgH+LlqUvSg907kaiS3v8Pma5kwpx5m1OjFd
 9Evfclq1BXxJ65gXKfg9OPuJfxxIFX3iAB1Eh0NYEl2eL1tZQv8E8w==
 =DiuH
Originator: austern@isolde.mti.sgi.com