Topic: N2369 library defect: Const-incorrect get_deleter function for


Author: AlbertoBarbati@libero.it (Alberto Ganesh Barbati)
Date: Fri, 28 Sep 2007 02:42:43 GMT
Raw View
Daniel Kr=C3=BCgler ha scritto:
>=20
> template<class D, class T> D* get_deleter(shared_ptr<T> const& p);
>=20
> <snip>
>=20
> Proposed resolution:
>=20
> Replace the declarations of get_deleter in the header <memory>
> synopsis of [memory] and in [ util.smartptr.getdeleter] by one of the
> following alternatives (A), (B), or (C):
>=20
> (A) A pair of overloads which preserve the constness of the owning
> shared_ptr (This reflects the praxis existing for
> unique_ptr::get_deleter):
>=20
> template<class D, class T> const D* get_deleter(shared_ptr<T> const&
> p);
> template<class D, class T> D* get_deleter(shared_ptr<T>& p);

If we want to follow the example of unique_ptr, then we might also
consider making get_deleter() a member function, instead of a free
function. I guess the rationale in Boost was to make a pair with the
free function get_pointer(), but the latter has not been included in the
latest draft...

> (B) Provide *only* the immutable variant. This would reflect the
> current
> praxis of container::get_allocator(), map::key_comp(), or
> map::value_comp.

Actually all those functions return a copy of the allocator/comparator.
That is good, because by returning a pointer or reference the user will
still have some level of access to the object that may be undesirable
(even const will not prevent the user to use const_cast).

Unfortunately we can't do that for shared_ptr, because get_deleter()
might fail if the deleter has the wrong type.

> template<class D, class T> const D* get_deleter(shared_ptr<T> const&
> p);
>=20
> (C) Just remove the function.

All three options have some value in it, IMHO. There is one more option
that might be considered:

(D) Replace it with two functions:

template <class D, class T> D get_deleter(shared_ptr<T> const&);
template <class D, class T> bool has_deleter(shared_ptr<T> const&);

The first one would throw if D is the wrong type, while the latter would
never throw. This approach would reflect the current praxis of
use_facet/has_facet, with the twist of returning the deleter by value as
container::get_allocator() do.

Just out of curiosity, I did a search on Google Code for get_deleter. I
got only 23 hits, most of which were declarations (as term of
comparison, shared_ptr returned 500 hits). Only few hits were actually
uses of get_deleter:

- a use in Boost.Python, for converting shared_ptr to some python-aware
smart pointer
- a use in make_shared, which don't count because make_shared could
actually be made friend of shared_ptr
- a couple of uses for diagnostic purposes, one of which in the
shared_ptr test suite itself

The first use is the only one really interesting, IMHO. Actually, the
code only checked if the result of get_deleter() was NULL or not, it did
not use the returned value in any other way. This use would be
compatible with options (A), (B) and (D).

Just my two eurocent,

Ganesh

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





Author: AlbertoBarbati@libero.it (Alberto Ganesh Barbati)
Date: Mon, 1 Oct 2007 17:44:27 GMT
Raw View
Daniel Kr=C3=BCgler ha scritto:
>=20
> (A) A pair of overloads which preserve the constness of the owning
> shared_ptr (This reflects the praxis existing for
> unique_ptr::get_deleter):
>=20
> template<class D, class T> const D* get_deleter(shared_ptr<T> const&
> p);
> template<class D, class T> D* get_deleter(shared_ptr<T>& p);

This solution is no better than the current wording, because it is prone
to circumvention:

template <class D, class T>
D* get_non_const_deleter_from_const_ptr(shared_ptr<T> const& p)
{
  shared_ptr<T> copy(p)
  return get_deleter(copy);
}

Ganesh

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





Author: brok@spam-trap-cop.net (Bronek Kozicki)
Date: Mon, 1 Oct 2007 17:48:43 GMT
Raw View
Alberto Ganesh Barbati wrote:
> (D) Replace it with two functions:
>
> template <class D, class T> D get_deleter(shared_ptr<T> const&);
> template <class D, class T> bool has_deleter(shared_ptr<T> const&);

This is a very nice idea.


B.


--
Remove -trap- when replying. Usun -trap- gdy odpisujesz.

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





Author: alfps@start.no ("Alf P. Steinbach")
Date: Mon, 1 Oct 2007 20:19:59 GMT
Raw View
* Alberto Ganesh Barbati:
> Daniel Kr=FCgler ha scritto:
>> (A) A pair of overloads which preserve the constness of the owning
>> shared_ptr (This reflects the praxis existing for
>> unique_ptr::get_deleter):
>>
>> template<class D, class T> const D* get_deleter(shared_ptr<T> const&
>> p);
>> template<class D, class T> D* get_deleter(shared_ptr<T>& p);
>=20
> This solution is no better than the current wording, because it is pron=
e
> to circumvention:
>=20
> template <class D, class T>
> D* get_non_const_deleter_from_const_ptr(shared_ptr<T> const& p)
> {
>   shared_ptr<T> copy(p)
>   return get_deleter(copy);
> }

Yes, I agree, and originally objected to including option (A) for=20
exactly that reason, but agreed to include it because it's better to=20
have all options on the table and not exclude in advance.

Over in [comp.lang.c++.moderated] Peter Dimov now has referred to a=20
primary use case for get_deleter, <url:=20
http://boost.org/libs/smart_ptr/sp_techniques.html#another_sp>.

This use case, including the example code offered, is compatible with=20
get_deleter returning a pointer to const, which seems to be an ideal=20
solution given the constraint of conforming to the current interface.

The use case of "release" of a unique() shared_ptr then requires a=20
const_cast, which as you noted (also over in [comp.lang.c++.moderated])=20
is a good way to discourage that in general, so that it will not be done=20
accidentally, while still allowing it and flagging it as low-level.

Finally, as I noted in response (over in [comp.lang.c++.moderated] :-) )


1. When get_deleter returns pointer to const object, wording that the=20
deleter is not stored as more cv-qualified than specified by the client=20
code should IMO ideally be introduced.

N2315 CURRENT WORDING =A720.6.6.2.1/9 [util.smartptr.shared.const]

   Requires: p shall be convertible to T*. D shall be CopyConstructible.
   The copy constructor and destructor of D shall not throw exceptions.
   The expression d(p) shall be well-formed, shall have well defined
   behavior, and shall not throw exceptions.

and N2315 CURRENT WORDING =A720.6.6.2.10/1 [util.smartptr.getdeleter],

where here "p" refers to the shared_ptr, not the raw pointer owned by=20
that shared_ptr,

   Returns: If p owns a deleter d of type cv-unqualified D, returns &d;
   otherwise returns 0. The returned pointer remains valid as long as
   there exists a shared_ptr instance that owns d. [Note: It is
   unspecified whether the pointer remains valid longer than that. This
   can happen if the implementation doesn=92t destroy the deleter until a=
ll
   weak_ptr instances that share ownership with p have been destroyed.
   =97endnote]

leaves it unspecified whether the deleter is stored cv-unqualified or=20
perhaps with added constness, hence also whether get_deleter will=20
succeed if the original deleter type was cv-unqualified D, and whether=20
using the result of a const_cast will have formally defined behavior.

And in passing, the term "owns" is not defined.  It's set in italics,=20
twice, in the paragraph about ~shared_ptr.  Whereas the convention in=20
the standard is to set it in italics where it's (implicitly) defined.

PROPOSED REPLACEMENT of =A720.6.6.2.1/9 [util.smartptr.shared.const]:

   Requires: p shall be convertible to T*. D shall be CopyConstructible.
   The copy constructor and destructor of D shall not throw exceptions.
   The expression d(p) shall be well-formed, shall have well defined
   behavior, and shall not throw exceptions.  A copy of d, called the
   shared_ptr's /deleter/, of exact type D (not more or less
   cv-qualified) is stored with the copy of p, i.e. shared between
   shared_ptr instances. The shared_ptr is said to /own/ the copies.

PROPOSED REPLACEMENT of  =A720.6.6.2.10/1 [util.smartptr.getdeleter]:

   Returns: If p owns a deleter d of exact type D, returns const_cast<D
   const*>(&d); otherwise returns 0. The returned pointer remains valid
   as long as there exists a shared_ptr instance that owns d. [Note: It
   is unspecified whether the pointer remains valid longer than that.
   This can happen if the implementation doesn=92t destroy the deleter
   until all weak_ptr instances that share ownership with p have been
   destroyed. =97endnote]

Proposed fix of italics-issue =A720.6.6.2.2/1 [util.smartptr.shared.dest]=
:

   <fix> See below. </fix>


2. It would be Really Really Nice if shared_ptr used std::default_delete=20
as default, instead of calling delete directly as default.

This would allow static specification of a default delete action for a=20
class, i.e. a per-class customization, in addition to and allowing one=20
to avoid using the current per-instance customization of specifying the=20
delete action at every instantiation of a shared_ptr.

N2315 CURRENT WORDING =A720.6.6.2.2/1 [util.smartptr.shared.dest]:

   1 Effects:
     =97 If *this is empty or shares ownership with another shared_ptr
       instance (use_count()>1), there are no side effects.
     =97 Otherwise, if *this /owns/ a pointer p and a deleter d, d(p) is
       called.
     =97 Otherwise, *this /owns/ a pointer p, and delete p is called.

PROPOSED REPLACEMENT of =A720.6.6.2.2/1 [util.smartptr.shared.dest],

changes are to remove italics and, in the last point, to use default_dele=
te:

   1 Effects:
     =97 If *this is empty or shares ownership with another shared_ptr
       instance (use_count()>1), there are no side effects.
     =97 Otherwise, if *this owns a pointer p and a deleter d, d(p) is
       called.
     =97 Otherwise, *this owns a pointer p, and std::default_delete<T>()(=
p)
       is called.  [Note: by default this calls delete p. -endnote]

where in N2315 std::default_delete is defined by =A720.6.5.1.1=20
[unique.ptr.dltr.dflt].

Cheers,

- Alf


Disclaimer, paraphrasing someone's default code header comment: if the=20
above is all Good, then it was written by me, otherwise, it's someone=20
else impersonating me.

--=20
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

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