Topic: additional ctor for auto_ptr


Author: Ian Haggard <ian@shellus.com>
Date: 1997/08/18
Raw View
Fergus Henderson <fjh@cs.mu.oz.au> writes:
> Ian Haggard <ian@shellus.com> writes:
> >Let's see if you can spot the hidden flaw here:
> >
> >struct rgb { unsigned char r,g,b; };
> >extern void bar(auto_ptr<rgb>);
> >void foo()
> >{
> >  static rgb color;
> >  auto_ptr<rgb> unowned_pcolor(&color);
> >  unowned_pcolor.release();
> >  bar(unowned_pcolor);
> >}
>
> >So what is the hidden flaw I was talking about?  Variables of type rgb
> >are not required to have any sort of alignment.  And if your
> >auto_ptr<rgb> puts the ownership bool in the low-order bit of its
> >pointer, you've just screwed yourself.
>
> That is not a flaw in the code above.  If auto_ptr<rgb> puts the
> ownership bool in the lower-order bit of its pointer for types that
> have no alignment requirement, then the auto_ptr implementation is
> broken.

I'm not so sure about that.  If you look at the December working paper,
it does not address the issue directly, but it seems to imply that
auto_ptr is permitted to put the ownership bool in the low-order bit of
its pointer as long as new always allocates an aligned object:
STD> 20.4.5  Template class auto_ptr
[lib.auto.ptr]
STD>
STD> 1 Template  auto_ptr  stores a pointer to an object obtained via
new and
STD>   deletes that object when it itself is destroyed (such as when
leaving
STD>   block scope _stmt.dcl_).
--
Ian Haggard  ||  ian@shellus.com (work)  ||  IanHaggard@juno.com (home)
GNU/Linux -- "Oh, no, Mr Bill!" || #define employer_opinion !my_opinion
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: spam@motu.com (David Abrahams)
Date: 1997/08/12
Raw View
On 12 Aug 97 12:12:19 GMT, Christian Millour
<chris161@club-internet.fr> wrote:

<snip>

>sigh. nobody seems to spot that the above code is actually a
>maintenance tool. let's rewrite it as a more neutral
>
>void third_party_code(auto_ptr<foo> const &);
>
>the above is a library function, over which I have absolutely
>no control. I use it in an application
>
>void my_code() {
>  auto_ptr<foo> apf(new foo);
>  ...
>  third_party_code(apf);
>}
>
>in a later release, I realize that I need to perform something
>with the pointee before it gets disposed of. Obviously this
>means that I want to keep control over its lifetime. If
>third_party_code() really needs to own the pointee (IMHO it
>shouldn't, more on this later), I'm stuck; meaning, I have to
>REWRITE third_party_code() ! If it doesn't, I can use
>
>void my_code() {
>  auto_ptr<foo> apf(new foo);
>  auto_ptr<foo> keeper(apf);
>  third_party_code(apf);
>  finalize(keeper);
>}
>
>and thus dance my way around the limitations in third_party_code,
>while still getting the usefull stuff done. Where's the evil in
>that ? And, perhaps more importantly, what are the alternatives ?

The evil shows up when third_party_code() is transferring ownership to
something that outlives the function call. In fact there is almost
never a good reason to write a function which accepts an auto_ptr<T>
as a parameter unless the T is going to outlive the function call - in
that case, why not just use a T& and let the caller allocate the T on
the stack or otherwise control its lifetime (using a local auto_ptr,
for example)?


Granted, there no ownership test, but there is a more important
problem: there is no existence test (there can never be one with a
lightweight smart pointer). What happens when a later call to
third_party_code2() tries to dereference the auto_ptr<T> that it has
stored away somewhere? Your keeper deleted the T all by itself long
before.

// third_party.cpp
static auto_ptr<foo> save;
void third_party_code(auto_ptr<foo> p) { save = p; }
void third_party_code2() { p->memberFunction(); }

Your maintainance tool is a hack that can only be used when you know
the internal workings of third_party_code(), and I suggest that the
appearance of auto_ptr<T> in a parameter list should be taken as a
hint that the lifetime of the T may outlive the function call. I think
your only _real_ alternative is negotiation with the vendor of your
third_party_code.

>Now, let me switch hats and put myself in the position of the
>writer of third_party_code(). As a responsible software writer,
>I try to anticipate my clients needs and I have to cope with
>the above, which means that I can be handled an auto_ptr that
>doesn't own its pointee. So, before doing anything drastic
>(such as deleting it) I first check that I can legitimely do
>so. OUCH! I CAN'T! It seems auto_ptr has a problem...
>
>Let's get back a bit and consider the fundamental reason why
>I have put an auto_ptr in third_party_code() interface. IMHO,
>the only valid reason is as a courtesy for the client, in those
>cases where an object lifetime is so closely related to the
>function call that it makes sense to bind both concepts.

I'm not sure what this means. I suggest that the only valid reason is
where there is a legitimate need for ownership to be transferred. In
all other cases, I favor giving the caller control over the lifetime
of the object. The caller shouldn't need to hack around an arbitrary
"claim of ownership" by the called function.

>The most
>strinking examples occur probably when the function is in fact
>a constructor. Anyway, the real meaning is
>  third_party_code(foo * pointee, bool owner);
>in most anticipated cases owner will be true but I can't outlaw
>the possibility that it might be false.

There are lots of places where we have to write requires clauses in
the interface comments for functions and hope that clients follow
them. This may be one of them. Unfortunately, abuse of interfaces is
impossible to prevent.

>As claimed earlier, if third_party_code really needs to own
>the pointee, then an auto_ptr cannot be used in its interface
>IMNSHO. Use an other beast, a bare foo*, an owning_ptr<foo>,
>whatever. Not an auto_ptr.

It is certainly a limitation of auto_ptr that it doesn't enforce
ownership when passed as a parameter. Unfortunately, I don't think
there is a way to do this and still maintain the traditional const
right-hand-side of constructor arguments.

>Back to the safety check. We cannot add an ownership test,
>because we'd be back to the beginning (having an operation on
>a const object modifying a public aspect of this object). I
>think a medium position can be reached, if we have release()
>return 0 when not owner. It is akin to, but not quite, an
>ownership test, and should satisfy all needs. Remember that
>the pointer value can in any cases be reached via get().

That would perhaps be useful. But I would also like the reset() member
function returned, which I found useful. Unfortunately, I think it's
too late to get the auto_ptr we all want (I doubt we even all want the
same thing). Fortunately, writing our own smart pointers is not so
hard...


(Please change 'spam' to 'abrahams' in the return address, which has been altered to foil junk mail senders.)
---
[ 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
]





Author: Christian Millour <chris161@club-internet.fr>
Date: 1997/08/12
Raw View
James Kanze wrote:
>
> Christian Millour <chris161@club-internet.fr> writes:
>
> |>  David Abrahams wrote:
> |>
> |>  > Personally, I liked the pointer-zeroing behavior better, but I know
> |>  > that the non-owner state was never a design goal and it wasn't
> |>  > intended to be exploited. The ownership state is a wart on the design,
> |>  > and would not encourage making that wart any larger.
> |>
> |>  #pragma crusade-mode(on)
> |>
> |>  Let's not call names. What I read in CD2 is that auto_ptr provides a
> |>  semantics of strict ownership. In that case, `wart' is spelled
> |>  R E Q U I R E M E N T or documented behavior, on which one may (and
> |>  is actually expected to, IMHO) build further.
>
> It's a wart, a result of a compromise which no one really likes, but
> everyone can live with.
>

it turns out that the compromise has side effects, with which we have
to deal. As illustrated, the biggest hitch comes from assignments in
other contexts than passing of parameters or returning values. I can't
foresee how they could be outlawed. As a consequence, I don't believe
that the true, pure, original spirit of auto_ptr can be kept.

IMHO, the major implication is that no function being handled an
auto_ptr may assume that it owns the pointee. Code using auto_ptr
should live with it. If an owned pointee is needed, then auto_ptr
is not the appropriate vector.

This is forced upon us by the language itself. There seem to be no
way, wart or not, to implement the simpler auto_ptr we'd like. Let's
live with that. It's not so bad, as auto_ptr can still be put to
useful uses.

<snippa>

> |>  Note that I am not suggesting to add an ownership test, or methods
> |>  to manipulate ownership directly, which would indeed compromise the
> |>  semantics.
> |>
> |>  > Finally, the intentional use of non-owner auto_ptrs is very dangerous.
> |>  > Functions receiving auto_ptrs as parameters are usually free to assume
> |>  > that they own the object received.
> |>              ***
> |>  Ah. This is new to me. Sounds like a rather obfuscated convention
> |>  though, and not one directly backed up by the current wording (see
> |>  also the question at the end of the post). I guess the function
> |>  could indeed stuff the pointee into a container that would outlive
> |>  both the function call and the scope of the original auto_ptr, and
> |>  that not owning the pointee could indeed be a problem. I'm not sure
> |>  though that this possibility alone should outlaw other uses of
> |>  auto_ptr.
> |>

all the more so that I am now convinced that auto_ptr is not appropriate
in such contexts. what's the point in having a ptr+(hidden)bool when a
ptr is enough and the bool is always true ?

> |>  I tend to view it the other way. Client code handling an auto_ptr
> |>  to a function should not expect to have a live pointee on return
> |>  of the function, unless it takes special measures. When interacting
> |>  with third-party code, the following might be useful:
> |>
> |>    auto_ptr<Soul> my_soul(...);
> |>    auto_ptr<Soul> keeper(my_soul);
> |>    deal_with_the_devil(my_soul); // tricked him. haha.
> |>    meet_St_Peter(keeper);
> |>    // now I'm dead but happy in paradise.
> |>
> |>  Is the above really that evil ? Should this use be prohibited ?
>
> It is certainly evil.  IMHO, it should be prohibited by the standard,
> but to do so would require violations of const that others find
> unacceptable.
>
> Note that amongst those who find the violation of const unacceptable,
> most seem to be in favor of returning to the very first definition, in
> which auto_ptr didn't support copy or assignment.  Their goal is NOT to
> support the above; their (understandable) position is simply that having
> the object on the right side of an assignment operator change value is
> too counter-intuitive to be allowed in the standard.
>
> You don't really think that the above code is "maintainable" in any way,
> do you?  On the other hand, you have a point.  It is what the standard
> defines.  And people will code on this assumption.  Which, IMHO, is a
> very strong argument against the current situation.

sigh. nobody seems to spot that the above code is actually a
maintenance tool. let's rewrite it as a more neutral

void third_party_code(auto_ptr<foo> const &);

the above is a library function, over which I have absolutely
no control. I use it in an application

void my_code() {
  auto_ptr<foo> apf(new foo);
  ...
  third_party_code(apf);
}

in a later release, I realize that I need to perform something
with the pointee before it gets disposed of. Obviously this
means that I want to keep control over its lifetime. If
third_party_code() really needs to own the pointee (IMHO it
shouldn't, more on this later), I'm stuck; meaning, I have to
REWRITE third_party_code() ! If it doesn't, I can use

void my_code() {
  auto_ptr<foo> apf(new foo);
  auto_ptr<foo> keeper(apf);
  third_party_code(apf);
  finalize(keeper);
}

and thus dance my way around the limitations in third_party_code,
while still getting the usefull stuff done. Where's the evil in
that ? And, perhaps more importantly, what are the alternatives ?

Now, let me switch hats and put myself in the position of the
writer of third_party_code(). As a responsible software writer,
I try to anticipate my clients needs and I have to cope with
the above, which means that I can be handled an auto_ptr that
doesn't own its pointee. So, before doing anything drastic
(such as deleting it) I first check that I can legitimely do
so. OUCH! I CAN'T! It seems auto_ptr has a problem...

Let's get back a bit and consider the fundamental reason why
I have put an auto_ptr in third_party_code() interface. IMHO,
the only valid reason is as a courtesy for the client, in those
cases where an object lifetime is so closely related to the
function call that it makes sense to bind both concepts. The most
strinking examples occur probably when the function is in fact
a constructor. Anyway, the real meaning is
  third_party_code(foo * pointee, bool owner);
in most anticipated cases owner will be true but I can't outlaw
the possibility that it might be false.

As claimed earlier, if third_party_code really needs to own
the pointee, then an auto_ptr cannot be used in its interface
IMNSHO. Use an other beast, a bare foo*, an owning_ptr<foo>,
whatever. Not an auto_ptr.

Back to the safety check. We cannot add an ownership test,
because we'd be back to the beginning (having an operation on
a const object modifying a public aspect of this object). I
think a medium position can be reached, if we have release()
return 0 when not owner. It is akin to, but not quite, an
ownership test, and should satisfy all needs. Remember that
the pointer value can in any cases be reached via get().

>                                               (FWIW: I think I
> prefer an auto_ptr without copy or assignment, or even a standard
> without auto_ptr, to the current situation.)
>

I'd be content with the single modification outlined above.
Otherwise, and unless a better solution is agreed upon, I'd
wholeheartedly second either position.
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Ian Haggard <ian@shellus.com>
Date: 1997/08/13
Raw View
In article <33f07308.89202556@news.motu.com>,
  David Abrahams <spam@motu.com> wrote:
>
> On 12 Aug 97 12:12:19 GMT, Christian Millour
> <chris161@club-internet.fr> wrote:
>
> <snip>
>
> >sigh. nobody seems to spot that the above code is actually a
> >maintenance tool. let's rewrite it as a more neutral
> >
> >void third_party_code(auto_ptr<foo> const &);
> >
> >the above is a library function, over which I have absolutely
> >no control. I use it in an application

Sigh.  Everybody BUT Christian Millour seems to have realized that far
from being a maintenance tool, the current auto_ptr is a maintenance
nightmare.  Even with his proposed modification to release(), auto_ptr
is still a major maintenance headache.  If you have code with owned and
unowned auto_ptr's scattered throughout it, then you're eventually going
to get to a chicken-and-the-egg situation where there are one or more
potential owners and sometimes one will outlive the others and sometimes
another will.  And then you're just going to have to rewrite your code
to use reference-counted pointers, which is what you should have done in
the first place.

> >
> >void my_code() {
> >  auto_ptr<foo> apf(new foo);
> >  ...
> >  third_party_code(apf);
> >}
> >
> >in a later release, I realize that I need to perform something
> >with the pointee before it gets disposed of. Obviously this
> >means that I want to keep control over its lifetime. If
> >third_party_code() really needs to own the pointee (IMHO it
> >shouldn't, more on this later), I'm stuck; meaning, I have to
> >REWRITE third_party_code() ! If it doesn't, I can use
> >
> >void my_code() {
> >  auto_ptr<foo> apf(new foo);
> >  auto_ptr<foo> keeper(apf);
> >  third_party_code(apf);
> >  finalize(keeper);
> >}
> >
> >and thus dance my way around the limitations in third_party_code,
> >while still getting the usefull stuff done. Where's the evil in
> >that ? And, perhaps more importantly, what are the alternatives ?
>
> The evil shows up when third_party_code() is transferring ownership to
> something that outlives the function call. In fact there is almost
> never a good reason to write a function which accepts an auto_ptr<T>
> as a parameter unless the T is going to outlive the function call - in
> that case, why not just use a T& and let the caller allocate the T on
> the stack or otherwise control its lifetime (using a local auto_ptr,
> for example)?
>
> Granted, there no ownership test, but there is a more important
> problem: there is no existence test (there can never be one with a
> lightweight smart pointer). What happens when a later call to
> third_party_code2() tries to dereference the auto_ptr<T> that it has
> stored away somewhere? Your keeper deleted the T all by itself long
> before.
>
> // third_party.cpp
> static auto_ptr<foo> save;
> void third_party_code(auto_ptr<foo> p) { save = p; }
> void third_party_code2() { p->memberFunction(); }
>
> Your maintainance tool is a hack that can only be used when you know
> the internal workings of third_party_code(), and I suggest that the
> appearance of auto_ptr<T> in a parameter list should be taken as a
> hint that the lifetime of the T may outlive the function call. I think
> your only _real_ alternative is negotiation with the vendor of your
> third_party_code.

David Abrahams is right on the ball here.  Christian -- please show us
an example where a non-owner auto_ptr does something useful in code
resembling your example.  There are very few cases I can think of where
a non-owner auto_ptr would be at all useful, and none of them resemble
your example.  Here are the cases where something similar to a non-owner
auto_ptr might be useful:

(1)
You want to be able to pass either heap data or non-heap data to a class
that
needs to delete the data if and only if it's on the heap.
(2)
See (1).

But the auto_ptr class doesn't even do very well at these uses.  Let's
see if you can spot the hidden flaw here:

struct rgb { unsigned char r,g,b; };
extern void bar(auto_ptr<rgb>);
void foo()
{
  static rgb color;
  auto_ptr<rgb> unowned_pcolor(&color);
  unowned_pcolor.release();
  bar(unowned_pcolor);
}

If you spotted the hidden flaw here, congratulations.  If you didn't, I
have put the answer at the end of this message.

>
> >Now, let me switch hats and put myself in the position of the
> >writer of third_party_code(). As a responsible software writer,
> >I try to anticipate my clients needs and I have to cope with
> >the above, which means that I can be handled an auto_ptr that
> >doesn't own its pointee. So, before doing anything drastic
> >(such as deleting it) I first check that I can legitimely do
> >so. OUCH! I CAN'T! It seems auto_ptr has a problem...
> >
> >Let's get back a bit and consider the fundamental reason why
> >I have put an auto_ptr in third_party_code() interface. IMHO,
> >the only valid reason is as a courtesy for the client, in those
> >cases where an object lifetime is so closely related to the
> >function call that it makes sense to bind both concepts.

This is just plain wrong.  The only valid reason to pass an auto_ptr to
third party code is because that code needs to take control of the
lifetime of the object being passed.  Otherwise, you should just pass a
raw pointer, a reference, an unowned_ptr, or somesuch.

> I'm not sure what this means. I suggest that the only valid reason is
> where there is a legitimate need for ownership to be transferred. In
> all other cases, I favor giving the caller control over the lifetime
> of the object. The caller shouldn't need to hack around an arbitrary
> "claim of ownership" by the called function.
Exactly.

>
> >The most
> >strinking examples occur probably when the function is in fact
   ^^^^^^^^^
Christian has almost got it right here.  The most __STINKING__ examples
of the use of the current auto_ptr DO USUALLY occur when the function is
a constructor.

> >a constructor. Anyway, the real meaning is
> >  third_party_code(foo * pointee, bool owner);
> >in most anticipated cases owner will be true but I can't outlaw
> >the possibility that it might be false.

At least if you're using the current auto_ptr, you can't outlaw that
possibility.  Ergo you should rarely if ever use the current auto_ptr.

> There are lots of places where we have to write requires clauses in
> the interface comments for functions and hope that clients follow
> them. This may be one of them. Unfortunately, abuse of interfaces is
> impossible to prevent.
>
> >As claimed earlier, if third_party_code really needs to own
> >the pointee, then an auto_ptr cannot be used in its interface
> >IMNSHO. Use an other beast, a bare foo*, an owning_ptr<foo>,
> >whatever. Not an auto_ptr.

I agree that the current auto_ptr should not be used by third party code
that really needs to own its pointee.  In fact, the current auto_ptr
should not be used at all.

> It is certainly a limitation of auto_ptr that it doesn't enforce
> ownership when passed as a parameter. Unfortunately, I don't think
> there is a way to do this and still maintain the traditional const
> right-hand-side of constructor arguments.

I agree here.  We're definitely going to have to choose between
non-const right-hand-side constructor/assignment arguments or no
constructor/assignement arguments.  This brings me to another question
I've been thinking about lately.  The reason an auto_ptr can't be
returned by value from a function is that the copy constructor needs to
be invoked by the function-return mechanism.  And the function-return
mechanism will only use a copy constructor that has a const argument on
its rhs.  So what are the issues that are preventing us from merely
extending the function-return mechanism to also permit use of a
copy-constructor that has a non-const argument on its rhs?  Then we
could go back the the version of auto_ptr with non-const
constructor/assignment arguments and maybe we could even forget this
stupid ownership boolean thing.

> >Back to the safety check. We cannot add an ownership test,
> >because we'd be back to the beginning (having an operation on
> >a const object modifying a public aspect of this object). I
> >think a medium position can be reached, if we have release()
> >return 0 when not owner. It is akin to, but not quite, an
> >ownership test, and should satisfy all needs. Remember that
> >the pointer value can in any cases be reached via get().
>
> That would perhaps be useful. But I would also like the reset() member
> function returned, which I found useful. Unfortunately, I think it's
> too late to get the auto_ptr we all want (I doubt we even all want the
> same thing). Fortunately, writing our own smart pointers is not so
> hard...

You're probably right that it is too late to fix the auto_ptr in the
standard.  In this case, I would certainly prefer a standard without an
auto_ptr.

So what is the hidden flaw I was talking about?  Variables of type rgb
are not required to have any sort of alignment.  And if your
auto_ptr<rgb> puts the ownership bool in the low-order bit of its
pointer, you've just screwed yourself.
--
Ian Haggard  ||  ian@shellus.com (work)  ||  IanHaggard@juno.com (home)
GNU/Linux -- "Oh, no, Mr Bill!" || #define employer_opinion !my_opinion
---
[ 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
]





Author: spam@motu.com (David Abrahams)
Date: 1997/08/14
Raw View
On 13 Aug 1997 10:48:16 PDT, Ian Haggard <ian@shellus.com> wrote:

<snip>

>> >a constructor. Anyway, the real meaning is
>> >  third_party_code(foo * pointee, bool owner);
>> >in most anticipated cases owner will be true but I can't outlaw
>> >the possibility that it might be false.
>
>At least if you're using the current auto_ptr, you can't outlaw that
>possibility.  Ergo you should rarely if ever use the current auto_ptr.
>> There are lots of places where we have to write requires clauses in
>> the interface comments for functions and hope that clients follow
>> them. This may be one of them. Unfortunately, abuse of interfaces is
>> impossible to prevent.
>>
>> >As claimed earlier, if third_party_code really needs to own
>> >the pointee, then an auto_ptr cannot be used in its interface
>> >IMNSHO. Use an other beast, a bare foo*, an owning_ptr<foo>,
>> >whatever. Not an auto_ptr.
>
>I agree that the current auto_ptr should not be used by third party code
>that really needs to own its pointee.  In fact, the current auto_ptr
>should not be used at all.
>

I think this is a consequence of the ability to pass it across
function boundaries. Can you design a smart_ptr<T> such that
third_party_code() is always guaranteed to receive a pointer which is
actually owned and non-zero? I don't think so.

foo ( smart_ptr<T> p )
{
  third_party_code( p ); // if ownership transferred here,
  third_party_code( p ); // p can't be the owner here - abuse!
}

Whether p is a non-owner or p is 0 in the second call, both
alternatives require a contract between caller and callee which isn't
enforceabe at compile-time. I think auto_ptr is more helpful than it
is vulnerable to abuse. We could make it a bit less vulnerable by
making sure that it complains of abuse at runtime (e.g. assert
ownership when dereferenced), but that is only a matter of degree.

>> It is certainly a limitation of auto_ptr that it doesn't enforce
>> ownership when passed as a parameter. Unfortunately, I don't think
>> there is a way to do this and still maintain the traditional const
>> right-hand-side of constructor arguments.
>
>I agree here.  We're definitely going to have to choose between
>non-const right-hand-side constructor/assignment arguments or no
>constructor/assignement arguments.  This brings me to another question
>I've been thinking about lately.  The reason an auto_ptr can't be
>returned by value from a function is that the copy constructor needs to
>be invoked by the function-return mechanism.  And the function-return
>mechanism will only use a copy constructor that has a const argument on
>its rhs.  So what are the issues that are preventing us from merely
>extending the function-return mechanism to also permit use of a
>copy-constructor that has a non-const argument on its rhs?  Then we
>could go back the the version of auto_ptr with non-const
>constructor/assignment arguments and maybe we could even forget this
>stupid ownership boolean thing.

Then auto_ptr wouldn't be useful in lots of template functions, etc.
which expect a const& argument and call assignment or copy
constructors. Anyway, messing with the core language semantics is a
murky business which is beyond my expertise. An easier alternative
would be to make the right-hand-side of the copy constructor and
assignment operator const, but make the pointer value mutable so it
could actually change. As I said before, though, this was considered
by many committee members to be too distasteful. In any case, none of
these options would prohibit the abuse you dislike.

<snip>

>You're probably right that it is too late to fix the auto_ptr in the
>standard.  In this case, I would certainly prefer a standard without an
>auto_ptr.

Some people are already counting on it. I personally find it more
useful than harmful. I don't see why it would need to die. If you
don't like it, don't use it, right?
(Please change 'spam' to 'abrahams' in the return address, which has been altered to foil junk mail senders.)
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Christian Millour <chris161@club-internet.fr>
Date: 1997/08/14
Raw View
David Abrahams wrote:
> The evil shows up when third_party_code() is transferring ownership to
> something that outlives the function call. In fact there is almost
> never a good reason to write a function which accepts an auto_ptr<T>
> as a parameter unless the T is going to outlive the function call - in
> that case, why not just use a T& and let the caller allocate the T on
> the stack or otherwise control its lifetime (using a local auto_ptr,
> for example)?
>
> Granted, there no ownership test, but there is a more important
> problem: there is no existence test (there can never be one with a
> lightweight smart pointer). What happens when a later call to
> third_party_code2() tries to dereference the auto_ptr<T> that it has
> stored away somewhere? Your keeper deleted the T all by itself long
> before.
>
> // third_party.cpp
> static auto_ptr<foo> save;
> void third_party_code(auto_ptr<foo> p) { save = p; }
> void third_party_code2() { p->memberFunction(); }
>

In that case I'd expect third_party_code to check ownership first.
With the suggested modification to release() one variation might be

  void third_party_code(auto_ptr<foo> p) {
    save = auto_ptr<foo>(p.release());
  }
  void third_party_code2() {
    assert(save.get()); /* if (save.get()) */
    save->memberFunction();
  }

Seems I can't find a less ugly one, which is indeed probably proof
enough that the idiom is evil ;-). Doesn't scale either. OK OK OK.

> Your maintainance tool is a hack that can only be used when you know
> the internal workings of third_party_code(), and I suggest that the
> appearance of auto_ptr<T> in a parameter list should be taken as a
> hint that the lifetime of the T may outlive the function call. I think
> your only _real_ alternative is negotiation with the vendor of your
> third_party_code.

Such as using a ref-counted ptr instead of auto_ptr, probably, as was
suggested by James Kanze. Or garbage collection. Hmmm.

OK. I'm convinced, non-owning auto_ptrs are a Bad Thing. I just wish
now there were a mean to assert them away (plus a hefty rationale
section in the draft !)

Thanks to all for your time and patience.
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Ian Haggard <ian@shellus.com>
Date: 1997/08/15
Raw View
In article <33f233a8.204067848@news.motu.com>,
  David Abrahams <spam@motu.com> wrote:
> On 13 Aug 1997 10:48:16 PDT, Ian Haggard <ian@shellus.com> wrote:
>
> <snip>
>
> >> >As claimed earlier, if third_party_code really needs to own
> >> >the pointee, then an auto_ptr cannot be used in its interface
> >> >IMNSHO. Use an other beast, a bare foo*, an owning_ptr<foo>,
> >> >whatever. Not an auto_ptr.
> >
> >I agree that the current auto_ptr should not be used by third party code
> >that really needs to own its pointee.  In fact, the current auto_ptr
> >should not be used at all.
> >
>
> I think this is a consequence of the ability to pass it across
> function boundaries. Can you design a smart_ptr<T> such that
> third_party_code() is always guaranteed to receive a pointer which is
> actually owned and non-zero? I don't think so.
> <snip>
I agree that in the general case (i.e. absence of garbage-collection, no
restrictions placed on the class being pointed to) this is not
possible.  But I personally have found that requiring "owned or zero" is
quite acceptable in practice.  Bugs involving dereferencing a null
pointer are a piece of cake to find and fix in comparison to bugs
involving erroneous ownership and/or dereferencing bad pointers.  Bugs
involving dereferencing a null pointer are quick to find (immediate
crash) and usually quick to fix.  Bugs involving erroneous ownership can
hide out for a long time before they're found, and once you find them,
often as not they mean that you should have been using ref-counting in
the first place, thus requiring significant code changes.

> >> It is certainly a limitation of auto_ptr that it doesn't enforce
> >> ownership when passed as a parameter. Unfortunately, I don't think
> >> there is a way to do this and still maintain the traditional const
> >> right-hand-side of constructor arguments.
> >
> >I agree here.  We're definitely going to have to choose between
> >non-const right-hand-side constructor/assignment arguments or no
> >constructor/assignement arguments.  This brings me to another question
> >I've been thinking about lately.  The reason an auto_ptr can't be
> >returned by value from a function is that the copy constructor needs to
> >be invoked by the function-return mechanism.  And the function-return
> >mechanism will only use a copy constructor that has a const argument on
> >its rhs.  So what are the issues that are preventing us from merely
> >extending the function-return mechanism to also permit use of a
> >copy-constructor that has a non-const argument on its rhs?  Then we
> >could go back the the version of auto_ptr with non-const
> >constructor/assignment arguments and maybe we could even forget this
> >stupid ownership boolean thing.
>
> Then auto_ptr wouldn't be useful in lots of template functions, etc.
> which expect a const& argument and call assignment or copy
> constructors. Anyway, messing with the core language semantics is a
> murky business which is beyond my expertise. An easier alternative
> would be to make the right-hand-side of the copy constructor and
> assignment operator const, but make the pointer value mutable so it
> could actually change. As I said before, though, this was considered
> by many committee members to be too distasteful. In any case, none of
> these options would prohibit the abuse you dislike.
I agree that the non-const ctor/assign rhs arguments do restrict
auto_ptr's usefulness in templates, but I don't see this as such a bad
thing.  Templates have to make assumptions about the semantics of the
operations they use, and in the case of auto_ptr, these assumptions will
not generally be true.  I prefer to find this out at compile-time rather
than getting potentially buggy behavior.

>
> <snip>
>
> >You're probably right that it is too late to fix the auto_ptr in the
> >standard.  In this case, I would certainly prefer a standard without an
> >auto_ptr.
>
> Some people are already counting on it. I personally find it more
> useful than harmful. I don't see why it would need to die. If you
> don't like it, don't use it, right?
The problem with "if you don't like it, don't us it" is that it JUST
DOESN'T WORK FOR LIBRARIES.  If you've ever been involved in library
design and/or maintenance (I have) then you know what I'm about to say:


(1)
Bad library code gets used.  No matter how poorly designed the class,
some bonehead client is going to use it.  And then when you want to fix
the stupid thing, you can't because it will break their code and make
them madder'n hell.

(2)
People learn programming idioms by looking at other peoples' designs and
other peoples' code -- especially other peoples' libraries.  STL and
InterViews are beautiful examples this happening in a good way.  But I
shudder to think of the young, impressionable minds that will be
influenced by auto_ptr.  Moreover, I shudder to think that I might
someday have to interface with third_party_code() written by these
young, impressionable minds.


If auto_ptr gets taken out of the standard, then vendors can just make
their own auto_ptr-like class in their own namespace and use it -- after
all, it's not like it's hard to write an auto_ptr.  But if auto_ptr gets
into the standard in the form it's currently in, then it's never going
to get fixed.  If you think people are relying on it now, then just wait
until the time they get around to updating the C++ standard.

The supreme irony of this whole auto_ptr thing is that auto_ptr was
originally designed simply to ensure local objects got destroyed when an
exception got thrown.  And exceptions were designed in part to reduce
the inefficiency caused by requiring general-case code to handle
exceptional cases.  But the current auto_ptr in the standard actually is
LESS efficient in the general case that raw pointers were.  One step
forward, two steps back...
--
Ian Haggard  ||  ian@shellus.com (work)  ||  IanHaggard@juno.com (home)
GNU/Linux -- "Oh, no, Mr Bill!" || #define employer_opinion !my_opinion
---
[ 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
]





Author: fjh@murlibobo.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/08/17
Raw View
Ian Haggard <ian@shellus.com> writes:

>Let's see if you can spot the hidden flaw here:
>
>struct rgb { unsigned char r,g,b; };
>extern void bar(auto_ptr<rgb>);
>void foo()
>{
>  static rgb color;
>  auto_ptr<rgb> unowned_pcolor(&color);
>  unowned_pcolor.release();
>  bar(unowned_pcolor);
>}

>So what is the hidden flaw I was talking about?  Variables of type rgb
>are not required to have any sort of alignment.  And if your
>auto_ptr<rgb> puts the ownership bool in the low-order bit of its
>pointer, you've just screwed yourself.

That is not a flaw in the code above.  If auto_ptr<rgb> puts the
ownership bool in the lower-order bit of its pointer for types that
have no alignment requirement, then the auto_ptr implementation is
broken.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Ian Haggard <ian@shellus.com>
Date: 1997/07/30
Raw View
I hope I'm not beating a dead horse here by taking so long to respond,
but I was out last week because my wife was busy having a baby.

James Kanze wrote:
> Ian Haggard <ian@shellus.com> writes:
>
> |>  I totally agree with you when you say that the semantics of a class
> |>  should be clear from its structure and members' signatures.  And the
> |>  fact that this is not the case with the current auto_ptr is definitely
> |>  the biggest problem here -- the problem we both want to solve, albeit in
> |>  different ways.
>
> I can't let this pass.
>
> The semantics of a class are almost never clear from its structure and
> its members' signatures.  auto_ptr is very simple, as classes go
> (forgetting the subtleties of ownership for the moment).  Still, there
> are pre- and post-conditions that generally cannot be documented only by
> the function names and class structure: to begin with, the pointer
> argument to the constructor must point to memory allocated by a
> non-placement, non-array new (i.e.: not just on the heap, but not
> allocated by malloc, or new[], or a placement new -- including 'new (
> nothrow )', I think).  This is an important precondition that MUST be
> documented.
You're totally right here.  I was rather hasty in saying that the
semantics of a class should be clear from its structure and members'
signatures.  Ideally, this would be the case.  But there are
unfortunately many cases where classes cannot be made
"self-documenting", despite one's best efforts.  Probably the best we
can strive for in many instances is to minimize the number and
complexity of pre- and post-conditions which are not made obvious by the
members' signatures -- and even just this is a VERY worthy goal.  Also,
I would amend what I said by adding that a class's structure should
certainly play a minimal (if any) role in helping one comprehend the
class.

> Perhaps one of the reasons why I'm more tolerant than others with
> regards to the older version of auto_ptr (which modified the value of an
> object passed to it by const reference) is that, since the function
> prototype isn't adequate, I depend more on comments than the signature
> itself.  (Of course, this isn't the total argument against the
> intermediate version.  Independantly of the const, many people would
> find it counter-intuitive that the object on the right hand side of an
> assignment gets modified.)
I agree here with your unease about the assignment/copy semantics of
auto_ptr.  I personally think the assignment/copy semantics of auto_ptr
are ugly in all versions of auto_ptr which have them, but I can't see
any way to make it prettier than it was in the version which took
non-const references as arguments to its copy and assignment ops.  At
least in those versions the compiler isn't being lied to; and the user,
when they see the signatures of the copy and assignment ops, will be
inclined to look at the documentation to understand what they're doing.

BTW which version of auto_ptr in which draft (other than the current
one) modified an object passed to it by const reference?  Or was this
just one of the proposed modifications to the original auto_ptr
proposal?

James Kanze also wrote:
> For the record: the "original" auto_ptr did not support copy or
> assignment at all; it was basically just meant to simplify exception
> safety in functions using the heap.  When the proposal was presented,
> some of the people who had real experience with such pointers pointed
> out that you often wanted a separate supplier or consumer for the
> object: i.e.: the pointer would be a return value or an argument.  In
> such cases, the supplier renders ownership, and the consumer aquires it.
>
> A second proposal was made, to provide this functionality.  Regretfully,
> it couldn't be made to work correctly without major violations of
> const.  (IMHO: the overall semantics are more important than the strict
> interpretation of const.  I can very well understand the opposite point
> of view however: if the function is declared as taking a const ref., the
> user doesn't expect it to modify the object referred to.)
>
> In the end: a significant number of people found it totally unacceptable
> that passing an object via a const reference resulted in an object whose
> value has changed, and a significant number of people (myself included)
> found it unacceptable to not be able to return an auto_ptr from a
> function, or to pass it to a function (in both cases, with change of
> ownership).  The current auto_ptr is the result.
I would be interested and honored to know what your evaluation is of the
modified version of auto_ptr for which I recently posted complete
source.  This version would allow auto_ptr's to be returned from
functions with, in my opinion, only a minimum of violation of const.

> Christian Millour <chris161@club-internet.fr> writes:
> |>    auto_ptr<Soul> my_soul(...);
> |>    auto_ptr<Soul> keeper(my_soul);
> |>    deal_with_the_devil(my_soul); // tricked him. haha.
> |>    meet_St_Peter(keeper);
> |>    // now I'm dead but happy in paradise.
> |>
> |>  Is the above really that evil ? Should this use be prohibited ?
>
> It is certainly evil.  IMHO, it should be prohibited by the standard,
> but to do so would require violations of const that others find
> unacceptable.
>
> Note that amongst those who find the violation of const unacceptable,
> most seem to be in favor of returning to the very first definition, in
> which auto_ptr didn't support copy or assignment.  Their goal is NOT to
> support the above; their (understandable) position is simply that having
> the object on the right side of an assignment operator change value is
> too counter-intuitive to be allowed in the standard.
I must admit that it I have been tempted in the past to think that
auto_ptr would be improved by removing copy and assignment from it
altogether.  But I have found copy and assignment altogether too useful
in my own auto_ptr-like class to think so any more...

> You don't really think that the above code is "maintainable" in any way,
> do you?  On the other hand, you have a point.  It is what the standard
> defines.  And people will code on this assumption.  Which, IMHO, is a
> very strong argument against the current situation.  (FWIW: I think I
> prefer an auto_ptr without copy or assignment, or even a standard
> without auto_ptr, to the current situation.)
I agree here that pretty much anything is preferable to the current
auto_ptr situation.  While an auto_ptr without copy or assignment would
be less than ideal, I could live with it.  I personally wouldn't find it
very useful, but at least I wouldn't have to worry about interfacing
with other people's code that used auto_ptr's in heinous ways.  The
current auto_ptr, however, is so repulsive that I can hardly find the
words to express my distaste.
--
Ian Haggard  ||  ian@shellus.com (work)  ||  IanHaggard@juno.com (home)
GNU/Linux -- "Oh, no, Mr Bill!" || #define employer_opinion !my_opinion
---
[ 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
]





Author: Alexandre Oliva <oliva@dcc.unicamp.br>
Date: 1997/07/23
Raw View
--Multipart_Wed_Jul_23_07:41:11_1997-1
Content-Type: text/plain; charset=US-ASCII

What do you people think of the following design?

I don't think auto_ptr should implement script ownership the way it
does, that is, by not providing any control about the ownership.  In
fact, stating clearly that there may be auto_ptr objects that are not
owners of the object they point to is an advantage, so there should be
mechanisms for controlling ownership.

One of the best advantages of this approach is that STL containers can
contain auto_ptr's.

I expect this implementation to be fully compliant with the current
definition, while it allows some more ownership control.

I've added two boolean data members: one for controlling ownership and
another for indicating the willingness of the auto_ptr to transfer
ownership (that will be maintained false if the auto_ptr is not the
owner of the object).

Since most pointer types in 32-bits architectures are usually 4-byte
aligned, these two bits could be encoded as the 2 least significant
bits of such pointers, just as the ownership bit is.

First of all, auto_ptr constructor accepts, in addition to a pointer,
a boolean value indicating the ownership and another indicating
whether the constructed auto_ptr should automatically release its
ownership when it is copied from or assigned from.

Copy-constructors behave the same way: if the copied-from object is
the owner of the object and intends to transfer ownership, it will
release the object and both its flags will be disabled.

The release method will only return a pointer to the object if it owns
the object; if it does not, it returns NULL.  This prevents the bad
usage of `delete release();' by whoever does not know whether the
auto_ptr is the owner of the object or not.

Four new function members were added: owns()const returns true iff the
auto_ptr owns the object; releases()const returns true iff it is
willing to release the object if copied; set_auto_release(bool = true)
can be used to enable or disable automatic release, as long as the
auto_ptr owns the object.  copy()const returns an auto_ptr that points
to the same object *this does, but that is certainly not the owner of
the object.  This may be useful for calling functions that accept
auto_ptrs without transferring ownership to them.


--Multipart_Wed_Jul_23_07:41:11_1997-1
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="auto_ptr.h"
Content-Transfer-Encoding: 7bit

// namespace std {

  template <class X> class auto_ptr {
  private:
    X* ptr;
    mutable bool owner; // indicates ownership
    mutable bool auto_release; // releases ownership when copied/assigned
    // (!owner) implies (!auto_release)
  public:
    typedef X element_type;

    explicit auto_ptr(X* p =0, bool owner_ = true, bool auto_release_ = true)
      throw()
      : ptr(p), owner(owner_), auto_release(auto_release_ && owner_) {}

    auto_ptr(const auto_ptr& p, bool owner_ = true, bool auto_release_ = true)
      throw()
      : ptr(p.get()), owner(owner_ && p.releases()),
 auto_release(owner && auto_release_) {
      if (owner)
 p.release();
    }

    template<class Y> auto_ptr(const auto_ptr<Y>& p,
          bool owner_ = true, bool auto_release_ = true)
      throw()
      : ptr(p.get()), owner(owner_ && p.releases()),
 auto_release(owner && auto_release_) {
      if (owner)
 p.release();
    }

    auto_ptr& operator=(const auto_ptr& p) throw() {
      if (this != &p) {
        delete release();
        ptr = p.get();
        auto_release = owner = p.releases();
        if (owner)
   p.release();
      }
      return *this;
    }

    template<class Y> auto_ptr& operator=(const auto_ptr<Y>& p) throw() {
      return *this = auto_ptr(p);
    }

    ~auto_ptr() { delete release(); }
    X& operator*() const throw() { return *ptr; }
    X* operator->() const throw() { return ptr; }
    X* get() const throw() { return ptr; }

    X* release() const throw() {
      if (!owner)
 return 0;
      auto_release = owner = false;
      return ptr;
    }

    bool owns() const { return owner; }
    bool releases() const { return auto_release; }
    void set_auto_release(bool enable = true) { release = enable && owns(); }
    auto_ptr copy() const { return auto_ptr(*this, false); }
  };

// }

--Multipart_Wed_Jul_23_07:41:11_1997-1
Content-Type: text/plain; charset=US-ASCII




--
Alexandre Oliva
mailto:oliva@dcc.unicamp.br mailto:aoliva@acm.org
Universidade Estadual de Campinas, SP, Brasil

--Multipart_Wed_Jul_23_07:41:11_1997-1--
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/07/23
Raw View
Ian Haggard <ian@shellus.com> writes:

|>  I totally agree with you when you say that the semantics of a class
|>  should be clear from its structure and members' signatures.  And the
|>  fact that this is not the case with the current auto_ptr is definitely
|>  the biggest problem here -- the problem we both want to solve, albeit in
|>  different ways.

I can't let this pass.

The semantics of a class are almost never clear from its structure and
its members' signatures.  auto_ptr is very simple, as classes go
(forgetting the subtleties of ownership for the moment).  Still, there
are pre- and post-conditions that generally cannot be documented only by
the function names and class structure: to begin with, the pointer
argument to the constructor must point to memory allocated by a
non-placement, non-array new (i.e.: not just on the heap, but not
allocated by malloc, or new[], or a placement new -- including 'new (
nothrow )', I think).  This is an important precondition that MUST be
documented.

Perhaps one of the reasons why I'm more tolerant than others with
regards to the older version of auto_ptr (which modified the value of an
object passed to it by const reference) is that, since the function
prototype isn't adequate, I depend more on comments than the signature
itself.  (Of course, this isn't the total argument against the
intermediate version.  Independantly of the const, many people would
find it counter-intuitive that the object on the right hand side of an
assignment gets modified.)

--
James Kanze   home:   kanze@gabi-soft.fr       +33 (0)1 39 55 85 62
              office: kanze@vx.cit.alcatel.fr  +33 (0)1 69 63 14 54
GABI Software, 22 rue Jacques-Lemercier, F-78000 Versailles, France
           -- Conseils en informatique industrielle --
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Christian Millour <chris161@club-internet.fr>
Date: 1997/07/16
Raw View
David Abrahams wrote:

> Personally, I liked the pointer-zeroing behavior better, but I know
> that the non-owner state was never a design goal and it wasn't
> intended to be exploited. The ownership state is a wart on the design,
> and would not encourage making that wart any larger.

#pragma crusade-mode(on)

Let's not call names. What I read in CD2 is that auto_ptr provides a
semantics of strict ownership. In that case, `wart' is spelled
R E Q U I R E M E N T or documented behavior, on which one may (and
is actually expected to, IMHO) build further.

Note that I am not suggesting to add an ownership test, or methods
to manipulate ownership directly, which would indeed compromise the
semantics.

> Finally, the intentional use of non-owner auto_ptrs is very dangerous.
> Functions receiving auto_ptrs as parameters are usually free to assume
> that they own the object received.
            ***
Ah. This is new to me. Sounds like a rather obfuscated convention
though, and not one directly backed up by the current wording (see
also the question at the end of the post). I guess the function
could indeed stuff the pointee into a container that would outlive
both the function call and the scope of the original auto_ptr, and
that not owning the pointee could indeed be a problem. I'm not sure
though that this possibility alone should outlaw other uses of
auto_ptr.

I tend to view it the other way. Client code handling an auto_ptr
to a function should not expect to have a live pointee on return
of the function, unless it takes special measures. When interacting
with third-party code, the following might be useful:

  auto_ptr<Soul> my_soul(...);
  auto_ptr<Soul> keeper(my_soul);
  deal_with_the_devil(my_soul); // tricked him. haha.
  meet_St_Peter(keeper);
  // now I'm dead but happy in paradise.

Is the above really that evil ? Should this use be prohibited ?

                                     If an object stores an auto_ptr in
> a member variable, it can usually rely on the existence of the
> pointed-to object until it is itself destroyed.

no objection here.

                                                 If you use non-owner
> auto_ptrs, all such bets are off..

not at all. It is the programmer's responsibility to ensure that the
pointed-to object exists if need be. In that sense passing auto_ptrs
around does require more attention than ref-counted ptrs. so ?

                                      In fact, I use a version of
> auto_ptr which assert()s if used when it doesn't own the pointer.

I bet you never use release() either.

All and all, your objections make a lot of sense but I think
restrict considerably the usefulness of auto_ptr. And if your
hard line indeed reflects the goals of the designers of auto_ptr,
I can't help wondering :

   why is auto_ptr<T>::release() public ?

am I the only one perceiving a contradiction ?
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Ian Haggard <ian@shellus.com>
Date: 1997/07/17
Raw View
> David Abrahams wrote:
>
> > Personally, I liked the pointer-zeroing behavior better, but I know
> > that the non-owner state was never a design goal and it wasn't
> > intended to be exploited. The ownership state is a wart on the design,
> > and would not encourage making that wart any larger.
>
> #pragma crusade-mode(on)
>
> Let's not call names. What I read in CD2 is that auto_ptr provides a
> semantics of strict ownership. In that case, `wart' is spelled
> R E Q U I R E M E N T or documented behavior, on which one may (and
> is actually expected to, IMHO) build further.
The standard is rather unclear here, but strict ownership seems to me to
mean that it either owns what it points to or it doesn't point to
anything (i.e. it points to NULL).  It seems to me that the standard is
implying that any other states are undefined.

> Note that I am not suggesting to add an ownership test, or methods
> to manipulate ownership directly, which would indeed compromise the
> semantics.
In what way would this compromise the semantics any more than they are
already compromised?  I would say that if you're going to have a
auto_ptr with the semantics that only one auto_ptr owns any object, but
multiple auto_ptr's may point to that object, you may as well go whole
hog.  But if I were to go whole hog like this, then not only would I add
the constructor you suggested that takes a boolean, I would also make
the assignment operator private and replace it with two member functions
with names something like
auto_ptr<T>::assign_with_ownership(auto_ptr<T>& src) and
auto_ptr<T>::assign_without_ownership(const auto_ptr<T>& src).  Then at
least the semantic trickiness of the class would be more obvious.  Or
heck, as long as we're mangling the auto_ptr class even further, why
don't we add a boolean that denotes whether an auto_ptr is willing to
transfer ownership of the thing it points to (i.e. auto_ptr(T*,bool
owner,bool transferable))?

> > Finally, the intentional use of non-owner auto_ptrs is very dangerous.
> > Functions receiving auto_ptrs as parameters are usually free to assume
> > that they own the object received.
>             ***
> Ah. This is new to me. Sounds like a rather obfuscated convention
> though, and not one directly backed up by the current wording (see
> also the question at the end of the post). I guess the function
> could indeed stuff the pointee into a container that would outlive
> both the function call and the scope of the original auto_ptr, and
> that not owning the pointee could indeed be a problem. I'm not sure
> though that this possibility alone should outlaw other uses of
> auto_ptr.
>
> I tend to view it the other way. Client code handling an auto_ptr
> to a function should not expect to have a live pointee on return
> of the function, unless it takes special measures. When interacting
> with third-party code, the following might be useful:
>
>   auto_ptr<Soul> my_soul(...);
>   auto_ptr<Soul> keeper(my_soul);
>   deal_with_the_devil(my_soul); // tricked him. haha.
>   meet_St_Peter(keeper);
>   // now I'm dead but happy in paradise.
>
> Is the above really that evil ? Should this use be prohibited ?
Yes to both your questions.  I think that the only way that maintenance
programmers who will have to deal with code that uses auto_ptr will be
able to maintain their sanity is if BOTH "Functions receiving auto_ptrs
as parameters are usually^H^H^H^H^H^H^Halways free to assume that they
own the object received" AND "Client code handling an auto_ptr to a
function should not expect to have a live pointee on return of the
function".  BTW, when the devil does a delete my_soul.release(); or
something similar, St. Peter won't be too happy about the empty husk of
a soul you've given him.  After all, the devil can't be relied on to
play nice, can he?

>                                      If an object stores an auto_ptr in
> > a member variable, it can usually rely on the existence of the
> > pointed-to object until it is itself destroyed.
>
> no objection here.
>
>                                                  If you use non-owner
> > auto_ptrs, all such bets are off..
>
> not at all. It is the programmer's responsibility to ensure that the
> pointed-to object exists if need be. In that sense passing auto_ptrs
> around does require more attention than ref-counted ptrs. so ?
This is a responsibility I would rather leave to the compiler except in
the miniscule number of cases where I actually DO NEED a smart pointer
class with the semantics that only one smart_pointer<T> owns an object,
but any number of smart_pointer<T>'s may point to that object.  That way
I can concentrate on writing code that does something useful rather than
worrying about whether the right auto_ptr owns my object.

>                                       In fact, I use a version of
> > auto_ptr which assert()s if used when it doesn't own the pointer.
>
> I bet you never use release() either.
> All and all, your objections make a lot of sense but I think
> restrict considerably the usefulness of auto_ptr. And if your
> hard line indeed reflects the goals of the designers of auto_ptr,
> I can't help wondering :
>
>    why is auto_ptr<T>::release() public ?
>
> am I the only one perceiving a contradiction ?
Actually, in the old version of auto_ptr that didn't have the ownership
boolean, the semantics of release were to return the T* pointed to by
the auto_ptr and set the auto_ptr to point to NULL.  In the current
version of auto_ptr -- with the inaccessible ownership bit -- release()
makes A LOT less sense than in the old version.  So, you've called
release() and now you have a T*.  Do you own that T* and consequently
the responsibility not to delete that T*?  Or do you not own that T* and
consequently if you were to delete that T* the results would be
undefined?  There's no way to tell.  Now let's bring that poor
maintenance programmer back into the scenario.  Suppose he's found a bug
in which a release()'d pointer was sometimes (but not always) deleted
when it wasn't supposed to be.  How does he fix that bug?  HE CAN'T.
Well, actually, he can fix the bug, but he's required either (a) to do
some non-portable offset-casting and/or bit-manipulation to get at the
auto_ptr's ownership boolean, (b) rewrite (potentially lots of) code to
use some smart_pointer class other than auto_ptr, or (c) edit the
version of auto_ptr in his compiler's header-files and add a member
function returning a boolean to tell you whether or not the auto_ptr
owns what it points to.  Now there's an ugly set of choices if I've ever
seen one.
--
Ian Haggard  ||  ian@shellus.com (work)  ||  IanHaggard@juno.com (home)
GNU/Linux -- "Oh, no, Mr Bill!" || #define employer_opinion !my_opinion
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Richard See <Richard.See@vega.co.uk>
Date: 1997/07/18
Raw View
Christian Millour wrote:
...
> the new version of auto_ptr allows to express transfer of ownership
> in an elegant, self-documented and potentially efficient way. Some
> manipulations are needed however to explicitely prevent ownership
> when it is not desired :
...
> The need for
> this hack could be avoided if auto_ptr provided an additional
> constructor, say
>
> auto_ptr(T * pointee, bool owner);

I'll call the result of this modification a Christian_Millour_ptr.

> I feel the potential of this constructor was overlooked because of
> the history of auto_ptr. Adding it even at this late date should be
> an easy decision to make, as it doesn't damage the existing
> semantics.
>
> So, what do you think ?

I think that elegant expression of ownership is easy (now I've seen it
done), it's expression of non-ownership that's potentially tricky,
because a pointer that's not owned might be invalid.

I think that there are a number of requirements that functions that take
or return pointers can have.  I agree that these requirements could be
self-documenting if an appropriate standard auto_ptr-like class was used
instead of a raw pointer.  However, the same class can't be used for
several different requirements, as otherwise the class chosen wouldn't
have the effect of documenting the requirements.  Here are some
requirements I can think of (not necessarily exhaustive or indicative of
good design):

class C;

// f1 modifies it's argument, and doesn't store the ptr.
void f1(C*);

// f2 stores the ptr for an indeterminate length of time before it's
// finished with it (possibly before the function returns).  f2 will
// ensure the object is deleted when it's finished with it.
void f2(C*);

// f3 stores the ptr at least until bar() is called, and maybe for
// some time beyond that.  This won't happen within this function call.
// f3 will ensure the object is deleted when it's finished with it.
void f3(C*);

// f4 stores the ptr for an indeterminate length of time before it's
// finished with it (possibly before the function returns).  f4 will
// not delete the object when it's finished with it.
void f4(C*);

// f5 generates a new C object, which must be deleted at some point.
C* f5();

// f6 returns a pointer to an object which is valid at least until bar()
// is called, and should not be deleted.
C* f6();

// f7 returns a pointer to a reference counted object.
// See C::removeReference for details.
C* f7();

Some of these requirements are not as straightforward as they may seem,
as it is not immediately obvious whether the deletions are occuring
because some side effect of the destructor being called is required, or
merely because this is the interface that the designer chose, and any
interface would do so long as a memory leak is avoided.  I'll assume the
first case.

Here's my opinion on roughly what class functionality would (largely)
allow these requirements to be implicit in the funtion declaration.

f1: fn takes raw pointer, as nothing but raw pointer functionality is
needed.

(Instead of using raw ptrs, a raw_ptr class could be used.  This would
achieve nothing except to document that the designer really did mean
that the ptr shouldn't be deleted, and the user isn't just looking at
ancient code from before the days of auto_ptrs.  I'm not sure they'd
catch on tho, and I'd certainly not advocate them in a language designed
from scratch when a T* already does the job.)

f2: fn takes an old style auto_ptr, that doesn't keep a potentially
invalid pointer after the function is called.

f3: could take old style auto_ptr or new style auto_ptr (new style would
be slightly easier to use in the case where the ptr is going to be used
following the function call).  In either case, documentation would still
be required to specify that the ptr remains valid at least until bar()
is called.

f4: yuck, when can you delete the object?  The function probably needs
to take some responsibility for deleting the object, using (for example)
reference counting or a Christian_Millour_ptr.

f5: could return an old style or new style auto_ptr.

f6: could return a raw ptr or a Christian_Millour_ptr with ownership set
to false.  (Couldn't return a new style auto_ptr with ownership set to
false, as the return value copy (ignoring optimisations) "copies the
pointer and transfers ownership to the destination"). A raw ptr would be
slightly more efficient.

f7: clearly needs to return a reference counting handler.

The point is that although a Christian_Millour_ptr may be useful for
function f4, it would need to be a supplement to auto_ptr not a
replacement, as the extra functionality would mean that the class could
not be used to define such a tight function interface.

As an aside, I prefer the old style auto_ptr to the new one as it allows
implicit documentation of the function f2, at no loss to the ease of
documenting f3, and appears slightly more efficient in time and space,
with little cost to the ease of implementation of callers of f3.  But
then I don't know all the arguments for why it was changed.

Richard.
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Christian Millour <chris161@club-internet.fr>
Date: 1997/07/18
Raw View
I'm reordering the post just a little bit.

Ian Haggard wrote:
> Christian Millour wrote:
> > David Abrahams wrote:
> >                                       In fact, I use a version of
> > > auto_ptr which assert()s if used when it doesn't own the pointer.
> >
> > I bet you never use release() either.
> > All and all, your objections make a lot of sense but I think
> > restrict considerably the usefulness of auto_ptr. And if your
> > hard line indeed reflects the goals of the designers of auto_ptr,
> > I can't help wondering :
> >
> >    why is auto_ptr<T>::release() public ?
> >
> > am I the only one perceiving a contradiction ?
> Actually, in the old version of auto_ptr that didn't have the ownership
> boolean, the semantics of release were to return the T* pointed to by
> the auto_ptr and set the auto_ptr to point to NULL.  In the current
> version of auto_ptr -- with the inaccessible ownership bit -- release()
> makes A LOT less sense than in the old version.  So, you've called
> release() and now you have a T*.  Do you own that T* and consequently
> the responsibility not to delete that T*?  Or do you not own that T* and
> consequently if you were to delete that T* the results would be
> undefined?  There's no way to tell.

my point exactly. I used to think that, as a consequence, release() was
not meant for public consumption in this version of auto_ptr. But see
also
the suggestion at the end of this post.

>                                     Now let's bring that poor
> maintenance programmer back into the scenario.  Suppose he's found a bug
> in which a release()'d pointer was sometimes (but not always) deleted
> when it wasn't supposed to be.  How does he fix that bug?  HE CAN'T.

take away release() from the public interface and the potential for the
bug disappears.

> > I tend to view it the other way. Client code handling an auto_ptr
> > to a function should not expect to have a live pointee on return
> > of the function, unless it takes special measures. When interacting
> > with third-party code, the following might be useful:
> >
> >   auto_ptr<Soul> my_soul(...);
> >   auto_ptr<Soul> keeper(my_soul);
> >   deal_with_the_devil(my_soul); // tricked him. haha.
> >   meet_St_Peter(keeper);
> >   // now I'm dead but happy in paradise.
> >
> > Is the above really that evil ? Should this use be prohibited ?
> Yes to both your questions.  I think that the only way that maintenance
> programmers who will have to deal with code that uses auto_ptr will be
> able to maintain their sanity is if BOTH "Functions receiving auto_ptrs
> as parameters are usually^H^H^H^H^H^H^Halways free to assume that they
> own the object received" AND "Client code handling an auto_ptr to a
> function should not expect to have a live pointee on return of the
> function".  BTW, when the devil does a delete my_soul.release(); or
> something similar, St. Peter won't be too happy about the empty husk of
> a soul you've given him.  After all, the devil can't be relied on to
> play nice, can he?

Same as above. without release() he's got no supported way to play
dirty (barring the truly devilish delete &(*my_soul); or
delete my_soul.get(); (=8-0))

The problem is, since there is no way to catch at compile time
  auto_ptr<T> a(...);
  auto_ptr<T> b(a);
  fun(a);
the strict ownership intended *cannot* be ensured.

Having to assert away (or throw an exception on) access to an unowned
pointee, as suggested by David Abrahams, is reasonable wrt to its
idea of what auto_ptr should be, but shouldn't be necessary. I
strongly believe that the semantics of a class should be clear from its
structure and member's signature, and should need only minimum extra
documentation. This is obviously not the case with the current auto_ptr.

If we can't have the semantics we want, let's live with the one we have.
Which I think would be (providing release is made private) :  multiple
auto_ptr can refer to the same pointee, at most one does own it, and it
is the programmer's responsibility to ensure that the owning auto_ptr
(if any) outlives the others.

With a private release(), once ownership has been granted to an
auto_ptr,
it cannot escape the auto_ptr population, i.e. it can be tranfered to
another
auto_ptr but not to the outside world. If such a transfer is indeed
needed
(examples, anyone ? ) then a public release() could be kept, but its
behavior altered to return 0 when the auto_ptr doesn't own the pointee
(this version obviously wouldn't be used to implement copy/assign).
Wouldn't this reconcile all views ? and legitimize my suggested
additional
constructor ?
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Ian Haggard <ian@shellus.com>
Date: 1997/07/19
Raw View
Christian Millour wrote:
> The problem is, since there is no way to catch at compile time
>   auto_ptr<T> a(...);
>   auto_ptr<T> b(a);
>   fun(a);
> the strict ownership intended *cannot* be ensured.
>
> Having to assert away (or throw an exception on) access to an unowned
> pointee, as suggested by David Abrahams, is reasonable wrt to its
> idea of what auto_ptr should be, but shouldn't be necessary. I
> strongly believe that the semantics of a class should be clear from its
> structure and member's signature, and should need only minimum extra
> documentation. This is obviously not the case with the current auto_ptr.
I totally agree with you when you say that the semantics of a class
should be clear from its structure and members' signatures.  And the
fact that this is not the case with the current auto_ptr is definitely
the biggest problem here -- the problem we both want to solve, albeit in
different ways.

> If we can't have the semantics we want, let's live with the one we have.
> Which I think would be (providing release is made private) :  multiple
> auto_ptr can refer to the same pointee, at most one does own it, and it
> is the programmer's responsibility to ensure that the owning auto_ptr
> (if any) outlives the others.
I would disagree with you that we can't get the semantics we want.  If
the semantics we want is to either own the object it points to or not
point to anything, then earlier in this thread I posted complete source
code for a working version of auto_ptr that had those semantics.
Basically, it is identical to the original auto_ptr class, except that
it fixed the one defect in the original -- that you couldn't return an
auto_ptr from a function.

> With a private release(), once ownership has been granted to an
> auto_ptr,
> it cannot escape the auto_ptr population, i.e. it can be tranfered to
> another
> auto_ptr but not to the outside world. If such a transfer is indeed
> needed
> (examples, anyone ? )
I can think of tons of examples in legacy code.  I know of lots of
classes that take raw pointers as arguments to their constructors and
member functions, expecting to own the pointers.  If I were to post all
such examples I have seen, I could fill up a book.  As just one example,
one class in one of Shell's internal libraries is a doubly-linked list
of pointers, whose constructor takes, among other arguments, a boolean
saying whether or not it should act as if it owns the pointers passed to
it.  This class is used extensively throughout the library.  If I were
to want to be able to use this library and also use auto_ptr's, I'd need
a release() function.

> then a public release() could be kept, but its
> behavior altered to return 0 when the auto_ptr doesn't own the pointee
> (this version obviously wouldn't be used to implement copy/assign).
> Wouldn't this reconcile all views ? and legitimize my suggested
> additional
> constructor ?
I think that your version of the release function is infinitely better
than the one in the standard -- it at least has a reasonable semantics.
But the question still remains as to what sematics auto_ptr should
have.  I believe that auto_ptr should have the semantics that an
auto_ptr either owns the object it points to, or it points to NULL.
Let's call this always_own semantics.  You believe that auto_ptr should
have the semantics that only one auto_ptr owns the object it points to,
but multiple auto_ptrs may point to the same object.  Let's call this
one_owner semantics.  The standard -- well, the auto_ptr in the standard
manages to combine the worst parts of both our semantics.  Let's call
this one_unknown_owner semantics.

I think either always_own semantics or one_owner semantics is way better
than one_unknown_owner semantics.  But I prefer that auto_ptr have
always_own semantics.  And I hope I can convince you to change your
preference.  Let me just give you a few reasons why I prefer always_own:

(1)
An always_own pointer completely frees the programmer from
responsibility for managing the lifetime of objects.  With one_owner,
the programmer need not worry about memory leaks, but he still has to
take responsibility for avoiding accesses to objects which are no longer
valid.

(2)
The contract between a caller and callee which pass or return a pointer
with always_own semantics is simpler.  In cases where either an
always_own pointer or a one_owner pointer would work, Occam's razor
tells us the always_own pointer should be preferred.  In these cases,
even if there were no other benefits, an always_own pointer better
documents the semantics of the function and provides some small degree
of compile-time protection against error on the part of the function
implementor.

(3)
But there are other benefits.  A one_owner pointer class cannot be
implemented as efficiently as an always_own pointer class.  The
always_own version beats the one_owner both in terms of speed and in
terms of space.  A platform-independent implementation of a one_owner
class takes up space for both a pointer and a boolean.  And if you are
on a platform on which you can use platform-dependent tricks to put the
boolean into the least-significant bit of the pointer, then you're
paying to have that bit stripped out every time you dereference the
pointer.

(4)
The reason auto_ptr was put into the standard was to provide a quick and
easy way of preventing memory leaks in the presence of exceptions.  For
example:
T* foo()
{ T* result=new T; do_stuff_with(result); return result; }
becomes:
auto_ptr<T> foo()
{ auto_ptr<T> result(new T); do_stuff_with(result.get()); return result;
}
And voila, your result is no longer leaked if do_stuff_with() throws an
exception.  Either an always_own pointer or a one_owner pointer will
suffice to do the job for which auto_ptr was put into the standard.
Going back to (3), this means that we should prefer that the auto_ptr in
the standard have always_own semantics.
--
Ian Haggard  ||  ian@shellus.com (work)  ||  IanHaggard@juno.com (home)
GNU/Linux -- "Oh, no, Mr Bill!" || #define employer_opinion !my_opinion
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Richard See <Richard.See@vega.co.uk>
Date: 1997/07/19
Raw View
Christian Millour wrote:
...
> With a private release(), once ownership has been granted to an
> auto_ptr,
> it cannot escape the auto_ptr population, i.e. it can be tranfered to
> another
> auto_ptr but not to the outside world. If such a transfer is indeed
> needed
> (examples, anyone ? ) then a public release() could be kept...

A public release funtion is needed so that code using auto_ptrs to
indicate ownership can interface with code that doesn't use auto_ptrs to
indicate ownership, such as old code.  It would seem selfish to limit
the usefulness of auto_ptrs to people writing completely new programs.
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: htrd@tcp.co.uk (Toby Dickenson)
Date: 1997/07/19
Raw View
Ian Haggard <ian@shellus.com> wrote:

 >>   auto_ptr<Soul> my_soul(...);
 >>   auto_ptr<Soul> keeper(my_soul);
 >>   deal_with_the_devil(my_soul); // tricked him. haha.
 >>   meet_St_Peter(keeper);
 >>   // now I'm dead but happy in paradise.
 >>
 >Yes to both your questions.
 >I think that the only way that maintenance
 >programmers who will have to deal with code that uses auto_ptr will be
 >able to maintain their sanity is if BOTH "Functions receiving auto_ptrs
 >as parameters are always free to assume that they
 >own the object received" AND "Client code handling an auto_ptr to a
 >function should not expect to have a live pointee on return of the
 >function".

Agreed.

 >Actually, in the old version of auto_ptr that didn't have the ownership
 >boolean, the semantics of release were to return the T* pointed to by
 >the auto_ptr and set the auto_ptr to point to NULL.  In the current
 >version of auto_ptr -- with the inaccessible ownership bit -- release()
 >makes A LOT less sense than in the old version.  So, you've called
 >release() and now you have a T*.  Do you own that T* and consequently
 >the responsibility not to delete that T*?  Or do you not own that T* and
 >consequently if you were to delete that T* the results would be
 >undefined?  There's no way to tell.  Now let's bring that poor
 >maintenance programmer back into the scenario.  Suppose he's found a bug
 >in which a release()'d pointer was sometimes (but not always) deleted
 >when it wasn't supposed to be.  How does he fix that bug?  HE CAN'T.
 >Well, actually, he can fix the bug, but he's required either (a) to do
 >some non-portable offset-casting and/or bit-manipulation to get at the
 >auto_ptr's ownership boolean, (b) rewrite (potentially lots of) code to
 >use some smart_pointer class other than auto_ptr, or (c) edit the
 >version of auto_ptr in his compiler's header-files and add a member
 >function returning a boolean to tell you whether or not the auto_ptr
 >owns what it points to.  Now there's an ugly set of choices if I've ever
 >seen one.

The real bug that needs fixing is where a non-owner auto_ptr was passed into a
function expecting an owner auto_ptr, violating the assumption you presented in
the first quoted paragraph.

In this case it may be inordinately difficult to track down where it this goes
wrong, if the scenario is equivalent to the deal_with_the_devil example. If the
older version of auto_ptr is used, the task is much easier since the
meet_St_Peter call would fail in a very obvious manner.

Is there every any use for a non-owner auto_ptr? If not, would it be prudent to
make the use of get() undefined if the auto_ptr is not an owner?




------------------------------------------------------------------------------
 Toby Dickenson                         PGP Key ID:  63CCA0A1
 htrd@tcp.co.uk                         Fingerprint: 45 B4 8A 79 67 03 1B 11
                                                     37 29 F5 5C B5 FD 2C A2
------------------------------------------------------------------------------
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: "Paul D. DeRocco" <pderocco@ix.netcom.crud.com>
Date: 1997/07/19
Raw View
Christian Millour wrote:

> All and all, your objections make a lot of sense but I think
> restrict considerably the usefulness of auto_ptr. And if your
> hard line indeed reflects the goals of the designers of auto_ptr,
> I can't help wondering :
>
>    why is auto_ptr<T>::release() public ?
>
> am I the only one perceiving a contradiction ?

I think the original purpose of auto_ptr was to eliminate lots of
try/catch blocks that were necessary to keep from losing dynamically
allocated memory in the face of exceptions. Frequently, one creates a
new something-or-other and stores a pointer to it into an auto_ptr while
doing some other stuff that might throw an exception. When done with the
other stuff, one copies the auto_ptr into a more permanent regular
pointer. At this point, it is necessary to remove ownership from the
auto_ptr.

Personally, I think that the whole concept of ownership is too fancy. I
prefer a stripped version (I call mine simply ptr<T>) which has no
concept of ownership. If its value isn't zero when it is destroyed, it
deletes its target, so releasing the target is done by setting the ptr
to zero. It doesn't allow you to do all the stuff that auto_ptr does,
but it does let you do the one most important thing, which is to hold a
pointer to an object that should go away if an exception occurs.

--

Ciao,
Paul

(Please remove the extra "crud" from the return address,
which has been altered to foil junk mail senders.)
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Tim Ottinger <tottinge@oma.com>
Date: 1997/07/19
Raw View
> > Finally, the intentional use of non-owner auto_ptrs is very
> dangerous.
> > Functions receiving auto_ptrs as parameters are usually free to
> assume
> > that they own the object received.
>             ***
> Ah. This is new to me. Sounds like a rather obfuscated convention
> though, and not one directly backed up by the current wording (see
> also the question at the end of the post).

I wrote a short paper somewhere long ago, and I think I posted it a few
places, on referent ownership. There were just four guidelines, so I
think that it's easy enough to reproduce here in short.

The problem I'm trying to address, of course, is "who owns the object".
The rules came from personal practice and from discussions in this very
newsgroup.  My suggestions were to ensure that you always know whether
you are a borrower or an owner of the object.

The difference is that only the owner ever has the right to delete an
object. All borrowers may be dependent on the object, but none have the
right to destroy it.

By tracking referent ownership, you ensure there are no
multiple-deletion errors, and that you do destroy the object when you
should.

1) Use a const reference or pass-by-value whenever possible for all
parameters. Prefer
    the reference.
2) Use pointers only when you really need to have a 'nullable
reference'. Otherwise, pointers
    are essentially obsolete as function parameters. References are just
as efficient, and easier
    to write with.
3) Return-by-reference and return-by-pointer are to be distrusted, and
should not be assumed
    to transfer ownership.
4) Only use auto_ptr<> to transfer ownership, and always use auto_ptr<>
to transfer
    ownership.

Of course, the auto_ptr<>s are used in exception-safe functions
normally, since those functions are the ones with the right to destroy
the heap objects.

If we use non-owner auto_ptr<>s then we are looking at a blurring of the
ownership lines. How can we explicitely transfer ownership? We have to
fall back on silly naming conventions and documentation again. It seems
a shame to give up a perfectly good idiom, so newly enabled, for any but
the most compelling of reasons.


> I guess the function
> could indeed stuff the pointee into a container that would outlive
> both the function call and the scope of the original auto_ptr, and
> that not owning the pointee could indeed be a problem. I'm not sure
> though that this possibility alone should outlaw other uses of
> auto_ptr.

Sounds good to me.

> I tend to view it the other way. Client code handling an auto_ptr
> to a function should not expect to have a live pointee on return
> of the function, unless it takes special measures

Actually, I really am suspicious of passing an auto_ptr<> to begin with,
because it's a little like outsourcing your central nervious system.
Normally, functions should be borrowers. Only in special circumstances
should one object assume ownership of another.

But if one object does, then we should be very careful about lying to
that object.

> When interacting with third-party code, the following might be useful:
>
>   auto_ptr<Soul> my_soul(...);
>   auto_ptr<Soul> keeper(my_soul);
>   deal_with_the_devil(my_soul); // tricked him. haha.
>   meet_St_Peter(keeper);
>   // now I'm dead but happy in paradise.

You left out my_soul.release()! Uh, oh. The devil killed your soul, and
StPeter didn't get anything!

> Is the above really that evil ? Should this use be prohibited ?

Probably. Probably.

>                                                  If you use non-owner
> > auto_ptrs, all such bets are off..
>
> not at all. It is the programmer's responsibility to ensure that the
> pointed-to object exists if need be.

See my blurb above. I feel that passing an auto_ptr<> when not giving
away ownership is a Bad Thing. Likewise, demanding to be passed an
auto_ptr<> when you don't _have_ to take ownership is likewise a Bad
Thing.

> All and all, your objections make a lot of sense but I think
> restrict considerably the usefulness of auto_ptr. And if your
> hard line indeed reflects the goals of the designers of auto_ptr,
> I can't help wondering :
>
>    why is auto_ptr<T>::release() public ?

Because some older code expects to take ownership via being passed a
pointer, not an auto_ptr<>. Hey, we didn't write all our code last week,
you know.

> am I the only one perceiving a contradiction ?

Backward compatibility is like that. C++ is like that. You do something
because it makes sense, and you leave a back door because someone might
need it. Usually people with legacy code to deal with.

But that isn't a contradiction really. It's a way of "grandfathering"
coding policies the way that the IRS "grandfathers" tax laws.

tim
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/07/23
Raw View
Christian Millour <chris161@club-internet.fr> writes:

|>  David Abrahams wrote:
|>
|>  > Personally, I liked the pointer-zeroing behavior better, but I know
|>  > that the non-owner state was never a design goal and it wasn't
|>  > intended to be exploited. The ownership state is a wart on the design,
|>  > and would not encourage making that wart any larger.
|>
|>  #pragma crusade-mode(on)
|>
|>  Let's not call names. What I read in CD2 is that auto_ptr provides a
|>  semantics of strict ownership. In that case, `wart' is spelled
|>  R E Q U I R E M E N T or documented behavior, on which one may (and
|>  is actually expected to, IMHO) build further.

It's a wart, a result of a compromise which no one really likes, but
everyone can live with.

For the record: the "original" auto_ptr did not support copy or
assignment at all; it was basically just meant to simplify exception
safety in functions using the heap.  When the proposal was presented,
some of the people who had real experience with such pointers pointed
out that you often wanted a separate supplier or consumer for the
object: i.e.: the pointer would be a return value or an argument.  In
such cases, the supplier renders ownership, and the consumer aquires it.

A second proposal was made, to provide this functionality.  Regretfully,
it couldn't be made to work correctly without major violations of
const.  (IMHO: the overall semantics are more important than the strict
interpretation of const.  I can very well understand the opposite point
of view however: if the function is declared as taking a const ref., the
user doesn't expect it to modify the object referred to.)

In the end: a significant number of people found it totally unacceptable
that passing an object via a const reference resulted in an object whose
value has changed, and a significant number of people (myself included)
found it unacceptable to not be able to return an auto_ptr from a
function, or to pass it to a function (in both cases, with change of
ownership).  The current auto_ptr is the result.

|>  Note that I am not suggesting to add an ownership test, or methods
|>  to manipulate ownership directly, which would indeed compromise the
|>  semantics.
|>
|>  > Finally, the intentional use of non-owner auto_ptrs is very dangerous.
|>  > Functions receiving auto_ptrs as parameters are usually free to assume
|>  > that they own the object received.
|>              ***
|>  Ah. This is new to me. Sounds like a rather obfuscated convention
|>  though, and not one directly backed up by the current wording (see
|>  also the question at the end of the post). I guess the function
|>  could indeed stuff the pointee into a container that would outlive
|>  both the function call and the scope of the original auto_ptr, and
|>  that not owning the pointee could indeed be a problem. I'm not sure
|>  though that this possibility alone should outlaw other uses of
|>  auto_ptr.
|>
|>  I tend to view it the other way. Client code handling an auto_ptr
|>  to a function should not expect to have a live pointee on return
|>  of the function, unless it takes special measures. When interacting
|>  with third-party code, the following might be useful:
|>
|>    auto_ptr<Soul> my_soul(...);
|>    auto_ptr<Soul> keeper(my_soul);
|>    deal_with_the_devil(my_soul); // tricked him. haha.
|>    meet_St_Peter(keeper);
|>    // now I'm dead but happy in paradise.
|>
|>  Is the above really that evil ? Should this use be prohibited ?

It is certainly evil.  IMHO, it should be prohibited by the standard,
but to do so would require violations of const that others find
unacceptable.

Note that amongst those who find the violation of const unacceptable,
most seem to be in favor of returning to the very first definition, in
which auto_ptr didn't support copy or assignment.  Their goal is NOT to
support the above; their (understandable) position is simply that having
the object on the right side of an assignment operator change value is
too counter-intuitive to be allowed in the standard.

You don't really think that the above code is "maintainable" in any way,
do you?  On the other hand, you have a point.  It is what the standard
defines.  And people will code on this assumption.  Which, IMHO, is a
very strong argument against the current situation.  (FWIW: I think I
prefer an auto_ptr without copy or assignment, or even a standard
without auto_ptr, to the current situation.)

--
James Kanze   home:   kanze@gabi-soft.fr       +33 (0)1 39 55 85 62
              office: kanze@vx.cit.alcatel.fr  +33 (0)1 69 63 14 54
GABI Software, 22 rue Jacques-Lemercier, F-78000 Versailles, France
           -- Conseils en informatique industrielle --
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/07/23
Raw View
Tim Ottinger <tottinge@oma.com> writes:

 |>  > > Finally, the intentional use of non-owner auto_ptrs is very
 |>  > dangerous.
 |>  > > Functions receiving auto_ptrs as parameters are usually free to
 |>  > assume
 |>  > > that they own the object received.
 |>  >             ***
 |>  > Ah. This is new to me. Sounds like a rather obfuscated convention
 |>  > though, and not one directly backed up by the current wording (see
 |>  > also the question at the end of the post).
 |>
 |>  I wrote a short paper somewhere long ago, and I think I posted it a few
 |>  places, on referent ownership. There were just four guidelines, so I
 |>  think that it's easy enough to reproduce here in short.
 |>
 |>  The problem I'm trying to address, of course, is "who owns the object".
 |>  The rules came from personal practice and from discussions in this very
 |>  newsgroup.  My suggestions were to ensure that you always know whether
 |>  you are a borrower or an owner of the object.
 |>
 |>  The difference is that only the owner ever has the right to delete an
 |>  object. All borrowers may be dependent on the object, but none have the
 |>  right to destroy it.
 |>
 |>  By tracking referent ownership, you ensure there are no
 |>  multiple-deletion errors, and that you do destroy the object when you
 |>  should.
 |>
 |>  1) Use a const reference or pass-by-value whenever possible for all
 |>  parameters. Prefer
 |>      the reference.
 |>  2) Use pointers only when you really need to have a 'nullable
 |>  reference'. Otherwise, pointers
 |>      are essentially obsolete as function parameters. References are just
 |>  as efficient, and easier
 |>      to write with.
 |>  3) Return-by-reference and return-by-pointer are to be distrusted, and
 |>  should not be assumed
 |>      to transfer ownership.
 |>  4) Only use auto_ptr<> to transfer ownership, and always use auto_ptr<>
 |>  to transfer
 |>      ownership.

Fine so far, but what about shared ownership.  I often have "chains" of
ownership, particularly for 'agent' classes.  Logically (at least most
of the time), I could use auto_ptr; in practice, I use a reference
counted pointer, so I don't even need to do the (simple) analysis that
it is a chain.

 |>  Of course, the auto_ptr<>s are used in exception-safe functions
 |>  normally, since those functions are the ones with the right to destroy
 |>  the heap objects.
 |>
 |>  If we use non-owner auto_ptr<>s then we are looking at a blurring of the
 |>  ownership lines. How can we explicitely transfer ownership? We have to
 |>  fall back on silly naming conventions and documentation again. It seems
 |>  a shame to give up a perfectly good idiom, so newly enabled, for any but
 |>  the most compelling of reasons.
 |>
 |>  > I guess the function
 |>  > could indeed stuff the pointee into a container that would outlive
 |>  > both the function call and the scope of the original auto_ptr, and
 |>  > that not owning the pointee could indeed be a problem. I'm not sure
 |>  > though that this possibility alone should outlaw other uses of
 |>  > auto_ptr.
 |>
 |>  Sounds good to me.
 |>
 |>  > I tend to view it the other way. Client code handling an auto_ptr
 |>  > to a function should not expect to have a live pointee on return
 |>  > of the function, unless it takes special measures
 |>
 |>  Actually, I really am suspicious of passing an auto_ptr<> to begin with,
 |>  because it's a little like outsourcing your central nervious system.
 |>  Normally, functions should be borrowers. Only in special circumstances
 |>  should one object assume ownership of another.

Not really.  Think of a client-server relationship.  The client creates
a request object, and passes it to the server.  From then on, it belongs
to the server (who may act as client to another server, and pass it
on).  Responses are handled in a similar manner, in the opposite
direction.  (Note that if the request and response objects are
streamable, the client and the server may even be on different
machines.  In such cases, the physical request object on the server side
is probably allocated somewhere in the communications stack, who
certainly doesn't want to know about when the application software which
it calls is finished with it.)

 |>  But if one object does, then we should be very careful about lying to
 |>  that object.
 |>
 |>  > When interacting with third-party code, the following might be useful:
 |>  >
 |>  >   auto_ptr<Soul> my_soul(...);
 |>  >   auto_ptr<Soul> keeper(my_soul);
 |>  >   deal_with_the_devil(my_soul); // tricked him. haha.
 |>  >   meet_St_Peter(keeper);
 |>  >   // now I'm dead but happy in paradise.
 |>
 |>  You left out my_soul.release()! Uh, oh. The devil killed your soul, and
 |>  StPeter didn't get anything!
 |>
 |>  > Is the above really that evil ? Should this use be prohibited ?
 |>
 |>  Probably. Probably.

Definitly, not probably.

 |>  >                                                  If you use non-owner
 |>  > > auto_ptrs, all such bets are off..
 |>  >
 |>  > not at all. It is the programmer's responsibility to ensure that the
 |>  > pointed-to object exists if need be.
 |>
 |>  See my blurb above. I feel that passing an auto_ptr<> when not giving
 |>  away ownership is a Bad Thing. Likewise, demanding to be passed an
 |>  auto_ptr<> when you don't _have_ to take ownership is likewise a Bad
 |>  Thing.

If the convention is that passing an auto_ptr means passing ownership,
then you are right, and it is a Bad Thing.  If there is some other
convention, then I will have to write a class of my own with this
convention (and I don't see much use of auto_ptr).

In practice, I will, at least for the forseeable future, avoid the
problem by continuing to use my reference counted pointer:-).  It may be
overkill, but it works.  (My particular implementation has the
constraint that the pointed-to object must derive from RefCntObj.  In
practice, however, I've found that this is not a problem.  In fact, in
cases like the requests and responses, above, I generally declare the
constructors private, and provide a static member function which returns
the reference counted pointer.  That way, there's no way the user can
"accidentally" get an object on the stack, or such.)

--
James Kanze   home:   kanze@gabi-soft.fr       +33 (0)1 39 55 85 62
              office: kanze@vx.cit.alcatel.fr  +33 (0)1 69 63 14 54
GABI Software, 22 rue Jacques-Lemercier, F-78000 Versailles, France
           -- Conseils en informatique industrielle --
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: "Paul D. DeRocco" <pderocco@ix.netcom.crud.com>
Date: 1997/07/11
Raw View
Christian Millour wrote:
>
> the new version of auto_ptr allows to express transfer of ownership
> in an elegant, self-documented and potentially efficient way. Some
> manipulations are needed however to explicitely prevent ownership
> when it is not desired :

Why would anyone want to create an auto_ptr<T> that doesn't own what it
points to? Isn't that what a T* is?

--

Ciao,
Paul

(Please remove the extra "crud" from the return address,
which has been altered to foil junk mail senders.)
---
[ 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
]





Author: Christian Millour <chris161@club-internet.fr>
Date: 1997/07/12
Raw View
Paul D. DeRocco wrote:
>
> Christian Millour wrote:
> >
> > the new version of auto_ptr allows to express transfer of ownership
> > in an elegant, self-documented and potentially efficient way. Some
> > manipulations are needed however to explicitely prevent ownership
> > when it is not desired :
>
> Why would anyone want to create an auto_ptr<T> that doesn't own what it
> points to? Isn't that what a T* is?
>

to be used as the source in an auto_ptr copy or assign, when we
don't want the destination to own the pointee.

I wrote:
> class Adopter {
>   auto_ptr<Child> p_;
> public:
>   Adopter(auto_ptr<Child> p) : p_(p) {}
>   ...
> };
>

An Adopter instance will own its child if constructed from an
auto_ptr that owns its pointee, and won't own it otherwise.

>   Adopter ha(auto_ptr<Child>(new Child));

here, ha owns an unnamed child which will be destroyed when ha
will be. well and good.

>   Child child;
>   Adopter sa(stack_ptr(&child));

here, I don't want sa to own its child, since the latter's
lifetime is handled separately. stack_ptr returns an
auto_ptr<Child> which doesn't own its pointee.

I wouldn't need stack_ptr if auto_ptr had the suggested additional
constructor. In that case I would just write

  Child child;
  Adopter sa(auto_ptr<Child>(&child, false));

I use this ownership idiom heavily, e.g. for interfaces that gets
destroyed when their subjet is (or the other way around). It is
IMHO a good alternative in cases where full-fledge reference counting
would be overkill or semantically confusing (in the sense that using
reference counting might suggest that several objects would reference
the pointee whereas by design at most one would). It also saves space
when I use a compact auto_ptr (which overlays the onership bit on the
pointer, as described in recents posts on c.l.c++.m).
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Alexandre Oliva <oliva@dcc.unicamp.br>
Date: 1997/07/12
Raw View
Paul D DeRocco writes:

> Christian Millour wrote:
>>
>> the new version of auto_ptr allows to express transfer of ownership
>> in an elegant, self-documented and potentially efficient way. Some
>> manipulations are needed however to explicitely prevent ownership
>> when it is not desired :

> Why would anyone want to create an auto_ptr<T> that doesn't own what it
> points to? Isn't that what a T* is?

There are two situations I can immediately think of where creating an
auto_ptr without ownership might be desirable:

1) when a class interface requires an auto_ptr<T>, not a T*, but you
don't want to transfer ownership to the class

2) when an auto_ptr<T> is to be created from another auto_ptr<T>, but
the ownership is not to be transferred

There are probably many others.

--
Alexandre Oliva
mailto:oliva@dcc.unicamp.br mailto:aoliva@acm.org
Universidade Estadual de Campinas, SP, Brasil
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: herbs@cntc.com (Herb Sutter)
Date: 1997/07/15
Raw View
Alexandre Oliva <oliva@dcc.unicamp.br> wrote:
>Paul D DeRocco writes:
>> Why would anyone want to create an auto_ptr<T> that doesn't own what it
>> points to? Isn't that what a T* is?

Exactly.

>There are two situations I can immediately think of where creating an
>auto_ptr without ownership might be desirable:
>
>1) when a class interface requires an auto_ptr<T>, not a T*, but you
>don't want to transfer ownership to the class

No.  If a class interface requires an auto_ptr<T> (and does not supply an
equivalent interface that uses a T*), then the class is by definition designed
to take ownership.  Circumventing this is equivalent to subverting the class
designer's intent... I'd put this in the same league as offset-casting to
access private members (you can do it, but it's evil and clearly a violation
of the class).

>2) when an auto_ptr<T> is to be created from another auto_ptr<T>, but
>the ownership is not to be transferred

No, that's what a T* is for.  You'd never create an auto_ptr that doesn't take
ownership... by definition, that's what an auto_ptr is!  If you want a pointer
that doesn't own the resource, then don't use an auto_ptr.  I think that
wanting to do either of the above indicates either a design problem or a
misunderstanding of what auto_ptr is for.

---
Herb Sutter (mailto:herbs@cntc.com)

Current Network Technologies Corp. (http://www.cntc.com)
2695 North Sheridan Way, Suite 150, Mississauga ON Canada   L5K 2N6
Tel 416-805-9088   Fax 905-822-3824
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: spam@motu.com (David Abrahams)
Date: 1997/07/15
Raw View
auto_ptr has been through several permutations, and I know some of the
history behind the way it works now. The fact that an auto_ptr can
point at something which it doesn't own is an artifact of the
interaction between two factors:

1. The semantics of returning values from functions requires that
those values have a copy constructor which is const on the value being
copied.

2. Transfer of ownership used to zero the pointer, but it was
considered too semantically ugly to make the pointer value mutable. If
the auto_ptr was to be const, it should continue to point at the same
object.

Personally, I liked the pointer-zeroing behavior better, but I know
that the non-owner state was never a design goal and it wasn't
intended to be exploited. The ownership state is a wart on the design,
and would not encourage making that wart any larger.

Finally, the intentional use of non-owner auto_ptrs is very dangerous.
Functions receiving auto_ptrs as parameters are usually free to assume
that they own the object received. If an object stores an auto_ptr in
a member variable, it can usually rely on the existence of the
pointed-to object until it is itself destroyed. If you use non-owner
auto_ptrs, all such bets are off.. In fact, I use a version of
auto_ptr which assert()s if used when it doesn't own the pointer.
(Please change 'spam' to 'abrahams' in the return address, which has been altered to foil junk mail senders.)
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: James Kuyper <kuyper@wizard.net>
Date: 1997/07/15
Raw View
Alexandre Oliva wrote:
 >
 > Paul D DeRocco writes:
 >
 > > Christian Millour wrote:
 > >>
 > >> the new version of auto_ptr allows to express transfer of ownership
 > >> in an elegant, self-documented and potentially efficient way. Some
 > >> manipulations are needed however to explicitely prevent ownership
 > >> when it is not desired :
 >
 > > Why would anyone want to create an auto_ptr<T> that doesn't own what it
 > > points to? Isn't that what a T* is?
 >
 > There are two situations I can immediately think of where creating an
 > auto_ptr without ownership might be desirable:
 >
 > 1) when a class interface requires an auto_ptr<T>, not a T*, but you
 > don't want to transfer ownership to the class
 >
 > 2) when an auto_ptr<T> is to be created from another auto_ptr<T>, but
 > the ownership is not to be transferred
 >

If the class interface specifies auto_ptr, doesn't that imply that the
class is expecting ownership transfers to occur? In that case, won't it
be a very bad idea to NOT transfer ownership?
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Ian Haggard <ian@shellus.com>
Date: 1997/07/16
Raw View
> auto_ptr has been through several permutations, and I know some of the
> history behind the way it works now. The fact that an auto_ptr can
> point at something which it doesn't own is an artifact of the
> interaction between two factors:
>
> 1. The semantics of returning values from functions requires that
> those values have a copy constructor which is const on the value being
> copied.
>
> 2. Transfer of ownership used to zero the pointer, but it was
> considered too semantically ugly to make the pointer value mutable. If
> the auto_ptr was to be const, it should continue to point at the same
> object.
>
> Personally, I liked the pointer-zeroing behavior better, but I know
> that the non-owner state was never a design goal and it wasn't
> intended to be exploited. The ownership state is a wart on the design,
> and would not encourage making that wart any larger.
I agree with you completely here.  I much prefer the original auto_ptr
design, and it will be a cold day in hell before I ever use the current
auto_ptr class that is in the standard.  Having said, that, I'd like to
re-iterate a suggestion I posted a while ago (for which Valentin Bonnard
deserves the credit) that would fix the problem of returning an auto_ptr
from a function:

auto_ptr.h:
-------------------
#include <assert.h>
#include <stddef.h>

template<class X> class auto_ptr_retval {
  X* px;
public:
  explicit auto_ptr_retval(X* p =NULL) : px(p) {}
  ~auto_ptr_retval() { if ( px ) delete px; }
  X* release() { X* tmp=px; px=NULL; return tmp; }
private:
  auto_ptr_retval& operator=(const auto_ptr_retval<X>&);
  auto_ptr_retval(const auto_ptr_retval<X>& ap);
};

template<class X> class auto_ptr {
  X* px;
public:
  // _lib.auto.ptr.cons_ construct/copy/destroy:
  explicit auto_ptr(X* p =NULL) : px(p) {}
  auto_ptr(auto_ptr<X>& ap) :px(ap.release()) {}
  auto_ptr(const auto_ptr_retval<X>& ap)
    :px(const_cast<auto_ptr_retval<X>&>(ap).release()) {}
  void operator=(auto_ptr<X>& ap) { if ( this!=&ap) reset(ap.release());
}
  void operator=(const auto_ptr_retval<X>& ap)
  { reset(const_cast<auto_ptr_retval<X>&>(ap).release()); }
  ~auto_ptr() { reset(); }
  // _lib.auto.ptr.members_ members:
  X& operator*() const { assert(px); return *px; }
  X* operator->() const { assert(px); return px; }
  X* get() const { return px; }
  X* release() { X* tmp=px; px=NULL; return tmp; }
  X* reset(X* p =NULL) { if( px && px!=p ) delete px; return (px=p); }

  operator bool() { return px; }
};

template<class X>
auto_ptr_retval<X> return_auto_ptr(auto_ptr<X> px)
{ return auto_ptr_retval<X>(px.release()); }



These changes would enable to following code to work:



#include <auto_ptr.h>
#include <stream.h>

// this works, too, but I would prefer to use the return_auto_ptr()
function
// auto_ptr_retval<int> new_int(int v)
// { return auto_ptr_retval<int>(new int(v)); }
auto_ptr_retval<int> new_int(int v)
{ return return_auto_ptr(auto_ptr<int>(new int(v))); }

void print_int(const auto_ptr<int>& i) { cout << *i << "\n"; }
void change_int(auto_ptr<int>& i,int v) { i=new_int(v); }
void destroy_int(auto_ptr<int> i) { cout << "Destroying "; print_int(i);
}

main() {
  new_int(1);
  print_int(new_int(2));
//  change_int(new_int(3),4); // error -- init non-const reference w/
temporary
  auto_ptr<int> i=new_int(5);
  change_int(i,6);
  destroy_int(i);
  destroy_int(new_int(7));
}


In here, the auto_ptr_retval class is still a wart, but at least it is
an obvious wart and it is a wart which is now almost totally outside the
auto_ptr class.  In this class, the user can still purposefully create
undefined/dangerous behavior by fooling with the auto_ptr_retval class,
but it's no longer possible to accidentally create undefined/dangerous
behavior by using the auto_ptr class.

> Finally, the intentional use of non-owner auto_ptrs is very dangerous.
> Functions receiving auto_ptrs as parameters are usually free to assume
> that they own the object received. If an object stores an auto_ptr in
> a member variable, it can usually rely on the existence of the
> pointed-to object until it is itself destroyed. If you use non-owner
> auto_ptrs, all such bets are off.. In fact, I use a version of
> auto_ptr which assert()s if used when it doesn't own the pointer.
I again agree here that the intentional use of non-owner auto_ptrs is
very dangerous.  The auto_ptr class was not designed to do this, and
even if you were to write a class that was designed to do this you would
still find that it created almost as many problems as it solved.  The
semantics of such a class would be confusing and bug-prone for all but
toy examples.

Personally, I have gotten around this problem by having a owner_ptr<T>
class similar to the auto_ptr<T> class I have shown above and a
unowned_ptr<T> class which is just a wrapper around a pointer that
prevents the user from accidentally deleting it.  My unowned_ptr<T>
class, BTW, does have a one-argument constructor that takes an
owner_ptr<T>.  I have created an unowned_ptr<T> because I disagree with
the people who have said that a T* is a pointer without ownership.  A T*
is a pointer whose ownership is unspecified at compile-time.  I prefer
to have ownership specified at compile-time and clear for all to see.
Would you rather pass important-data-that-mustn't-be-deleted to a
function that has the signature void do_stuff_with(T*) or would you
prefer a function that has the signature void
do_stuff_with(unowned_ptr<T>)?  I know which I prefer.

If you use the idiom I have herein suggested in your code, then you will
find that most of the functions to which you were passing an auto_ptr<T>
without ownership really should take an unowned_ptr<T> as their argument
and the rest are subtle bugs waiting to happen.  And yes, you can still
pass an auto_ptr<T> or an owner_ptr<T> or whatever you want to call it
to these functions.  You can even pass an owner_ptr<T> which is a
temporary to these functions.  It will just be implicitly converted into
an unowned_ptr<T>.
--
Ian Haggard  ||  ian@shellus.com (work)  ||  IanHaggard@juno.com (home)
GNU/Linux -- "Oh, no, Mr Bill!" || #define employer_opinion !my_opinion
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Christian Millour <chris161@club-internet.fr>
Date: 1997/07/16
Raw View
Herb Sutter wrote:
>
> Alexandre Oliva <oliva@dcc.unicamp.br> wrote:
> >Paul D DeRocco writes:
> >> Why would anyone want to create an auto_ptr<T> that doesn't own what it
> >> points to? Isn't that what a T* is?
>
> Exactly.
>
> >There are two situations I can immediately think of where creating an
> >auto_ptr without ownership might be desirable:
> >
> >1) when a class interface requires an auto_ptr<T>, not a T*, but you
> >don't want to transfer ownership to the class
>
> No.  If a class interface requires an auto_ptr<T> (and does not supply an
> equivalent interface that uses a T*), then the class is by definition designed
> to take ownership.

If systematic ownership is wanted, auto_ptr is not needed,
just pass the pointer and document the behavior.

But why settle for systematic ownership, when it can be
optional ?

If you agree that optional ownership is desirable, how do
you implement it ?

class Adopter {
  Adopter(Child * child) : child_(child), owner_(child) {} // takes
ownership
  Adopter(Child & child) : child_(&child), owner_(false) {} // don't
take ownership
  ~Adopter() { if (owner_) delete child_; }
  ...
private:
  Child * child_;
  bool owner_;
};

I find the above verbose and confusing, when compared to

class Adopter {
  Adopter(auto_ptr<Child> child) : child_(child) {}
private:
  auto_ptr<Child> child_;
};

                     Circumventing this is equivalent to subverting the
class
> designer's intent... I'd put this in the same league as offset-casting to
> access private members (you can do it, but it's evil and clearly a violation
> of the class).
>

What if you run into

foo(...) {
  ...
  auto_ptr<Child> child(some_child_source(...));
  ...
  Adopter a1(child); // takes ownership if nobody did since child
declaration
  ...
  Adopter a2(child); // won't take ownership ever. so what ?
  ...
}

I don't consider the above design faulty. It certainly doesn't
subvert my intent in designing Adopter ;-). It uses auto_ptr
to ensure that the Child will be destroyed when no more needed.
Ownership by a1 or a2 is dictated by context.

>
> >2) when an auto_ptr<T> is to be created from another auto_ptr<T>, but
> >the ownership is not to be transferred
>

now, this is getting muddy and I would try not to wade into
such waters.

> No, that's what a T* is for.  You'd never create an auto_ptr that doesn't take
> ownership... by definition, that's what an auto_ptr is!  If you want a pointer
> that doesn't own the resource, then don't use an auto_ptr.  I think that
> wanting to do either of the above indicates either a design problem or a
> misunderstanding of what auto_ptr is for.
>

please outline the design problem in foo() above.
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Christian Millour <chris161@club-internet.fr>
Date: 1997/07/09
Raw View
Hi all.

the new version of auto_ptr allows to express transfer of ownership
in an elegant, self-documented and potentially efficient way. Some
manipulations are needed however to explicitely prevent ownership
when it is not desired :

class Child {...};
class Adopter {
  auto_ptr<Child> p_;
public:
  Adopter(auto_ptr<Child> p) : p_(p) {}
  ...
};

// builds an auto_ptr that doesn't own its pointee
template <class T>
auto_ptr<T>
stack_ptr(T * x) {
  auto_ptr<T> p(x);
  p.release();
  return p;
}

int main {
  Adopter ha(auto_ptr<Child>(new Child));

  Child child;
  Adopter sa(stack_ptr(&child));
}

stack_ptr does the job but inefficiently (I doubt optimizers are
able to collapse the turning on and off of ownership). The need for
this hack could be avoided if auto_ptr provided an additional
constructor, say

auto_ptr(T * pointee, bool owner);

I feel the potential of this constructor was overlooked because of
the history of auto_ptr. Adding it even at this late date should be
an easy decision to make, as it doesn't damage the existing
semantics.

So, what do you think ? Does anyone care to champion a proposal ?
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]