Topic: Transfer functionality for shared_ptr


Author: cbarron3@ix.netcom.com (Carl Barron)
Date: Mon, 15 Oct 2007 13:32:15 GMT
Raw View
"Alf P. Steinbach" <alfps@start.no> wrote:

>
> No, you have misunderstood.
>
> Completely.
>
> With a non-const get_deleter, and with you the creator of the
> shared_ptr, the code above is unnecessary: all you need to do then is to
> install a no-op deleter function.
>
 no the user can do this:
   void screw_up(void *);

  // implementation:
  void screw_up(void *)
  {
    install_virus();
  }

  or something similiar:)
 with a typed non_const deleter you such as previously mentioned the
ONLY thing the user can do is turn of a delete p, not fill my harddisks
forever or provide a denial of service attack, etc.


 Now either this queuing system is a class or a collection of functions
either way all that needs doing is providing the deleter type to the
enquing operation [the one creating a shared_ptr in the first place]
and the getter of the raw ptr.  Rest of queue code is the same as now.
[the beauty of deleter's is shared_ptr<T> is the same type regardless
of what deleter is used:)]   If this small change is impossible then
good luck!!.

  Further I mean the contract is easy to test for and if it fails the
deep copy approach is only safe solution, at least as far as memory
allocation/deallocation goes.  Probably safer to throw and catch an
exception of some kind if the contract fails passing the shared_ptr
in the exception class.

---
[ 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: Fri, 12 Oct 2007 21:19:18 GMT
Raw View
If you have a shared_ptr, perhaps with a custom deleter of unknown type,
what extra functionality is required to then transfer that shared_ptr's
raw pointer and deleter to some other smart pointer?

In order to answer that question I created the small test program listed
below, where the class SharedPtr is a simplified variant of shared_ptr
(only features relevant to this problem).

The transferTo() member function seems to require, as shown here, two
member functions values() and clear() which would be very dangerous,
breaking encapsulation, if exposed to client code.

In other words, the transferTo() member function seems to be safe and
useful functionality that can only reasonably be implemented by
shared_ptr itself, and is not currently present.

I see no way to implement transferTo() using only the current public
functionality of shared_ptr.


<code>
// Example of transferTo() (a.k.a. "release") functionality for shared
// pointer.

// I had to fix up some formatting due to line-wrapping in posting.
// ...

#include    <iostream>
#include    <ostream>

#include    <cstddef>       // std::size_t
#include    <utility>       // std::pair
#include    <stdexcept>

#include    <boost/intrusive_ptr.hpp>
#include    <boost/function.hpp>
#include    <boost/type_traits/remove_const.hpp>

template< typename T >
void say( T const& v ) { std::cout << v << std::endl; }

namespace ext
{
     namespace detail
     {
         template< typename T >
         class AbstractRefCounter
         {
         private:
             std::size_t     myRefCount;
         public:
             typedef boost::function< void (T const*) >  AbstractDeleter;
             typedef std::pair< T*, AbstractDeleter >    InternalValues;

             AbstractRefCounter(): myRefCount( 0 ) {}
             virtual ~AbstractRefCounter() {}
             void addRef()   { ++myRefCount; }
             void release()  { if( --myRefCount == 0 ) { delete this; } }
             std::size_t count() const { return myRefCount; }

             virtual InternalValues values() const = 0;
             virtual void clear() = 0;
         };

         template< typename T >
         void intrusive_ptr_add_ref( AbstractRefCounter<T>* p )
         { p->addRef(); }

         template< typename T >
         void intrusive_ptr_release( AbstractRefCounter<T>* p )
         { p->release(); }

         template< typename T >
         void defaultDeleter( T const* p ) { delete p; }

         template< typename T, typename D >
         class DeleterInvoker
         {
         public:
             typedef typename boost::remove_const<T>::type NcT;

             D deleter;

             DeleterInvoker( D d ): deleter( d ) {}
             void operator()( T const* p ) const
             {
                 deleter( const_cast<NcT*>( p ) );
             }
         };

         template< typename T, typename D >
         class RefCounter: public AbstractRefCounter<T>
         {
         private:
             T*                      myPtr;
             DeleterInvoker<T, D>    myDeleter;
         public:
             typedef typename AbstractRefCounter<T>::InternalValues
                 InternalValues;

             RefCounter( T* p, D d ): myPtr( p ), myDeleter( d ) {}
             ~RefCounter() { if( myPtr != 0 ) { myDeleter( myPtr ); } }

             InternalValues values() const
             {
                 return InternalValues( myPtr, myDeleter );
             }

             void clear() { myPtr = 0; }
         };
     }

     template< typename T >
     class SharedPtr
     {
     private:
         typedef boost::intrusive_ptr< detail::AbstractRefCounter<T> >
             RefCountedPtr;
         RefCountedPtr   myPtr;
     public:
         typedef typename detail::AbstractRefCounter<T>::InternalValues
             InternalValues;

         SharedPtr(): myPtr() {}

         template< typename D >
         SharedPtr( T* p, D d )
             : myPtr( new detail::RefCounter<T, D>( p, d ) )
         {}
         bool isUnique() const
         { return (!myPtr? false : myPtr->count() == 1); }

         template< typename ValuesReceiver >
         ValuesReceiver transferTo()
         {
             if( !isUnique() )
             {
                 throw std::logic_error(
                     "SharedPtr::transferTo: is not unique()"
                     );
             }
             InternalValues const values = myPtr->values();
             myPtr->clear();
             return ValuesReceiver( values );
         }
     };
}    // namespace ext

template< typename T >
void destroy( T* p )
{
     say( "automatically deleted" );
     delete p;
}

enum TransferTestEnum{ testTransferSuccess, testTransferFailure };

void cppMain( TransferTestEnum whichTest )
{
     typedef ext::SharedPtr<int> IntPtr;

     IntPtr  p( new int( 42 ), destroy<int> );
     IntPtr  q;

     if( whichTest == testTransferFailure ) { q = p; }
     say( "main" );
     IntPtr::InternalValues const values =
         p.transferTo<IntPtr::InternalValues>();
     say( *values.first );       // 42
     delete values.first;
     say( "manually deleted" );
}

int main( int nArgs, char*[] )
{
     try
     {
         cppMain(
             nArgs == 1? testTransferSuccess : testTransferFailure
             );
         return EXIT_SUCCESS;
     }
     catch( std::exception const& x )
     {
         std::cerr << "!" << x.what() << std::endl;
         return EXIT_FAILURE;
     }
}
</code>


<output arguments="">
main
42
manually deleted
</output>

<output arguments="whatever">
main
automatically deleted
!SharedPtr::transferTo: is not unique()
</output>


--
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                      ]





Author: "Nevin :-] Liber" <nevin@eviloverlord.com>
Date: Fri, 12 Oct 2007 17:49:18 CST
Raw View
In article <13gvk7441t5baf0@corp.supernews.com>,
 alfps@start.no ("Alf P. Steinbach") wrote:

> If you have a shared_ptr, perhaps with a custom deleter of unknown type,
> what extra functionality is required to then transfer that shared_ptr's
> raw pointer and deleter to some other smart pointer?

If you want to transfer it to another shared_ptr, swap() should do it
(with a reset() to clear out the old value of the shared_ptr taking
ownership).

If you want to transfer it to something that is not a shared_ptr, this
seems like a bad idea.  After all, shared_ptr is maintaining its own
class invariants (such as the ref count across all instances pointing to
an object).  If you let any arbitrary class or function muck with it,
they really aren't invariants anymore.

This is different than, say, auto_ptr::release(), as it isn't
maintaining any invariants across multiple instances.

> In other words, the transferTo() member function seems to be safe and
> useful functionality that can only reasonably be implemented by
> shared_ptr itself, and is not currently present.

I suppose there might be a use for a shared_ptr::release() when
use_count() == 1.  Do you have something in mind?

--
 Nevin ":-)" Liber  <mailto:nevin@eviloverlord.com>  773 961-1620

---
[ 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: dgsteffen@numerica.us (Dave Steffen)
Date: Fri, 12 Oct 2007 23:12:16 GMT
Raw View
Oddly enough, this came up over here yesterday...

"Nevin :-] Liber" <nevin@eviloverlord.com> writes:

> In article <13gvk7441t5baf0@corp.supernews.com>,
>  alfps@start.no ("Alf P. Steinbach") wrote:
>
> > If you have a shared_ptr, perhaps with a custom deleter of unknown type,
> > what extra functionality is required to then transfer that shared_ptr's
> > raw pointer and deleter to some other smart pointer?
[...]
>
> If you want to transfer it to something that is not a shared_ptr, this
> seems like a bad idea.  After all, shared_ptr is maintaining its own
> class invariants (such as the ref count across all instances pointing to
> an object).  If you let any arbitrary class or function muck with it,
> they really aren't invariants anymore.
[...]

> I suppose there might be a use for a shared_ptr::release() when
> use_count() == 1.  Do you have something in mind?

  Our use case is this: we have a chunk of code that stores shared
  pointers in a queue.  We now pop these shared pointers off the queue
  in function A and pass them into a 3rd party library that offers a
  very C-ish interface; we send in void*'s (argh).  Later, in function
  B, we get the void*'s back and can call delete on them.

  The problem is that when we pop the shared pointers off the queue in
  function A, they go out of scope at the end of the function call and
  delete what they hold.

  Now, we know for an absolute fact that in function A use_count() ==
  1.  We can, of course, copy the underlying information out and work
  with that, but that seems wasteful.  We'd like to get a pointer to
  the data out of the shared pointer and relieve the shared pointer of
  the responsibility for cleaning it up.

  ... that is, more-or-less exactly what was described.

  The solution we're pursuing right now is to change what we store in
  the queue (e.g. don't store smart pointers).  Arguably we shouldn't
  be using them anyway, in this case, but going back to raw pointers
  instead really goes against the grain. :-)

--
Dave Steffen, Ph.D.         "I say we invite opportunity inside
Software Engineer IV         for a nice cup of tea, then hit her
Numerica Corporation         on the head and steal her purse."
ph (970) 461-2000 x227           -- Shlock Mercenary
dgsteffen at numerica dot us

---
[ 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: "Alf P. Steinbach" <alfps@start.no>
Date: Fri, 12 Oct 2007 19:15:32 CST
Raw View
* Nevin :-] Liber:
> In article <13gvk7441t5baf0@corp.supernews.com>,
>  alfps@start.no ("Alf P. Steinbach") wrote:
>
>> If you have a shared_ptr, perhaps with a custom deleter of unknown type,
>> what extra functionality is required to then transfer that shared_ptr's
>> raw pointer and deleter to some other smart pointer?
>
> If you want to transfer it to another shared_ptr, swap() should do it
> (with a reset() to clear out the old value of the shared_ptr taking
> ownership).

Simple assignment seems much simpler in that case.  Am I missing
something?  Or do you perhaps assume that swap() will change the
referred to raw pointer & deleter (AFAIK it won't)?


> If you want to transfer it to something that is not a shared_ptr, this
> seems like a bad idea.  After all, shared_ptr is maintaining its own
> class invariants (such as the ref count across all instances pointing to
> an object).  If you let any arbitrary class or function muck with it,
> they really aren't invariants anymore.
> This is different than, say, auto_ptr::release(), as it isn't
> maintaining any invariants across multiple instances.

"He proceeded. As each item was mentioned, new symbols sprang to life at
his touch, and melted into the basic function which expanded and changed.
   Gaal stopped him only once. 'I don't see the validity of that
set-transformation.'
   Seldon repeated it more slowly.
   Gaal said, 'But that is done by way of a forbidden socio-operation.'
   'Good. You are quick, but not yet quick enough.  It is not forbidden
in this connection.  Let me do it by expansions.'
   The procedure was much longer and at its end, Gaal said, humbly,
'Yes, I see now.'"


>> In other words, the transferTo() member function seems to be safe and
>> useful functionality that can only reasonably be implemented by
>> shared_ptr itself, and is not currently present.
>
> I suppose there might be a use for a shared_ptr::release() when
> use_count() == 1.  Do you have something in mind?

Mostly what I have in mind is the imperfect world of practical
programming.  So it might well be the case that some function f produces
a shared_ptr, that is guaranteed unique(), and function g takes as
argument a BalalaikaPtr, and you want to say g( f() ) and forget about
it, but you have no control over f and g.  For example.

Cheers,

- Alf

--
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                      ]





Author: "Alf P. Steinbach" <alfps@start.no>
Date: Fri, 12 Oct 2007 23:56:38 CST
Raw View
* Dave Steffen:
> Oddly enough, this came up over here yesterday...
>
> "Nevin :-] Liber" <nevin@eviloverlord.com> writes:
>
>> In article <13gvk7441t5baf0@corp.supernews.com>,
>>  alfps@start.no ("Alf P. Steinbach") wrote:
>>
>>> If you have a shared_ptr, perhaps with a custom deleter of unknown type,
>>> what extra functionality is required to then transfer that shared_ptr's
>>> raw pointer and deleter to some other smart pointer?
> [...]
>> If you want to transfer it to something that is not a shared_ptr, this
>> seems like a bad idea.  After all, shared_ptr is maintaining its own
>> class invariants (such as the ref count across all instances pointing to
>> an object).  If you let any arbitrary class or function muck with it,
>> they really aren't invariants anymore.
> [...]
>
>> I suppose there might be a use for a shared_ptr::release() when
>> use_count() == 1.  Do you have something in mind?
>
>   Our use case is this: we have a chunk of code that stores shared
>   pointers in a queue.  We now pop these shared pointers off the queue
>   in function A and pass them into a 3rd party library that offers a
>   very C-ish interface; we send in void*'s (argh).  Later, in function
>   B, we get the void*'s back and can call delete on them.
>
>   The problem is that when we pop the shared pointers off the queue in
>   function A, they go out of scope at the end of the function call and
>   delete what they hold.

If you /control/ the creation of the shared_ptr then you can do it using
the current interface, because you then control the deleter type.

However, I've argued strongly elsewhere for removing the most low-level
"easy" way to do that (namely the possibility of directly changing the
deleter), because it's low level, error prone and dangerous, only easy
like a goto, and that's now or will become Defect Report 730.

A higher level way to do it, still within the current functionality, is
to at the lowest implementation detail level use a deleter functor with
a mutable cancel member,

   namespace detail
   {
       template< typename T >
       class CancellableDeleter
       {
       private:
           mutable bool iShouldDelete;
       public:
           CancellableDeleter(): iShouldDelete( true ) {}
           void operator()( T const* p ) const
           { if( iShouldDelete ) { delete p; }
           void cancel() const { iShouldDelete = false; }
       };
   }

and then encapsulate this internal extreme abuse of 'mutable' in a
higher level which abstracts the "release",

   template< typename T >
   class SharedPtr: public boost::shared_ptr
   {
   private:
       typedef boost::shared_ptr Base;
       typedef detail::CancellableDeleter<T> Deleter;
   public:
       SharedPtr( T* p ): Base( p, Deleter() ) {}

       T* release()
       {
           if( !unique() ) { throw std::logic_error( "not unique!" ); }
           T* const result = get();
           Deleter const* const d = get_deleter<Deleter>( *this );
           if( d == 0 ) { throw std::runtime_error( "nknwn dltr!" ); }
           d->cancel();
           reset();
           return result;
       }
   };

the point being that this SharedPtr will be compatible with a possible
future const result of get_deleter, and can easily be reimplemented in
terms of a proper transfer_to() operation, if or when... :-)

Disclaimer: off-the-cuff code, not touched by compiler's hands.

Cheers,

- Alf


--
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                      ]





Author: cbarron3@ix.netcom.com (Carl Barron)
Date: Sat, 13 Oct 2007 16:29:23 GMT
Raw View
"Alf P. Steinbach" <alfps@start.no> wrote:

> In other words, the transferTo() member function seems to be safe and
> useful functionality that can only reasonably be implemented by
> shared_ptr itself, and is not currently present.
>
> I see no way to implement transferTo() using only the current public
> functionality of shared_ptr.
  If you create the shared_ptr then its easy, Use a custom deleter
with a tunable action such as

    struct may_be_delete
    {
        bool do_it;
        may_be_delete():do_it(true){}
        void operator () (T *p)
        { if(do_it) delete p;}
    };

    now get_deleter<may_be_delete>(p) will return a non const ptr
to the deleter. allowing do_it to be changed to false. The result
is when the shared_ptr's dtor is called nothing is done to the ptr
it contains.
   You just showed an example of why get_deleter<>() returns a non const
ptr.:)
  Of course if the deleter is wrong or it is not unique then
the copy approach is all there is.

---
[ 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: nevin@eviloverlord.com ("Nevin :-] Liber")
Date: Sat, 13 Oct 2007 16:30:00 GMT
Raw View
In article <qqmabqoaqc3.fsf@hafnium.numerica.us>,
 dgsteffen@numerica.us (Dave Steffen) wrote:

>   Our use case is this: we have a chunk of code that stores shared
>   pointers in a queue.  We now pop these shared pointers off the queue
>   in function A and pass them into a 3rd party library that offers a
>   very C-ish interface; we send in void*'s (argh).  Later, in function
>   B, we get the void*'s back and can call delete on them.

It sounds like what you really want is unique_ptr when it is available.

>   The problem is that when we pop the shared pointers off the queue in
>   function A, they go out of scope at the end of the function call and
>   delete what they hold.
>
>   Now, we know for an absolute fact that in function A use_count() ==
>   1.  We can, of course, copy the underlying information out and work
>   with that, but that seems wasteful.  We'd like to get a pointer to
>   the data out of the shared pointer and relieve the shared pointer of
>   the responsibility for cleaning it up.

Another possibility is, when you take them off the queue, store them in
a set<shared_ptr<T> > (or maybe a map<T*, shared_ptr<T> >, if you have
to do lookup by raw pointer).  Instead of calling delete on them when
you are done, call s.erase(p).  The advantage to this is that you can
still have a custom deleter.  The disadvantage is that it is O(log N),
not O(1).

>   The solution we're pursuing right now is to change what we store in
>   the queue (e.g. don't store smart pointers).  Arguably we shouldn't
>   be using them anyway, in this case, but going back to raw pointers
>   instead really goes against the grain. :-)

Still another choice is to use a Boost Pointer Container
<http://www.boost.org/libs/ptr_container/doc/ptr_container.html>, which
has the advantage of less overhead than shared_ptr.

Regards

--
 Nevin ":-)" Liber  <mailto:nevin@eviloverlord.com>  773 961-1620

---
[ 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: Sun, 14 Oct 2007 06:39:32 GMT
Raw View
* Carl Barron:
> "Alf P. Steinbach" <alfps@start.no> wrote:
>
>> In other words, the transferTo() member function seems to be safe and
>> useful functionality that can only reasonably be implemented by
>> shared_ptr itself, and is not currently present.
>>
>> I see no way to implement transferTo() using only the current public
>> functionality of shared_ptr.
>   If you create the shared_ptr then its easy

If you don't create it then it's not easy.  My article was about the
"not easy" case.  The easy case isn't much to remark about.


>, Use a custom deleter
> with a tunable action such as
>
>     struct may_be_delete
>     {
>         bool do_it;
>         may_be_delete():do_it(true){}
>         void operator () (T *p)
>         { if(do_it) delete p;}
>     };

Yes, I noted that elsewere in reply to Dave Steffen, and gave code using
about the same idea, only a bit more const correct both in operator()
etc. (const correctness for argument to operator() is important because
there are cases where you want to store T const* in a shared_ptr).


>     now get_deleter<may_be_delete>(p) will return a non const ptr
> to the deleter. allowing do_it to be changed to false. The result
> is when the shared_ptr's dtor is called nothing is done to the ptr
> it contains.
>    You just showed an example of why get_deleter<>() returns a non const
> ptr.:)

No, you have misunderstood.

Completely.

With a non-const get_deleter, and with you the creator of the
shared_ptr, the code above is unnecessary: all you need to do then is to
install a no-op deleter function.


>   Of course if the deleter is wrong or it is not unique then
> the copy approach is all there is.

I'm sorry, that's not meaningful to me.


Cheers, & hth..,

- Alf

--
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                      ]





Author: howard.hinnant@gmail.com (Howard Hinnant)
Date: Sun, 14 Oct 2007 06:40:44 GMT
Raw View
In article <qqmabqoaqc3.fsf@hafnium.numerica.us>,
 dgsteffen@numerica.us (Dave Steffen) wrote:

> Oddly enough, this came up over here yesterday...
>
> "Nevin :-] Liber" <nevin@eviloverlord.com> writes:
>
> > In article <13gvk7441t5baf0@corp.supernews.com>,
> >  alfps@start.no ("Alf P. Steinbach") wrote:
> >
> > > If you have a shared_ptr, perhaps with a custom deleter of unknown type,
> > > what extra functionality is required to then transfer that shared_ptr's
> > > raw pointer and deleter to some other smart pointer?
> [...]
> >
> > If you want to transfer it to something that is not a shared_ptr, this
> > seems like a bad idea.  After all, shared_ptr is maintaining its own
> > class invariants (such as the ref count across all instances pointing to
> > an object).  If you let any arbitrary class or function muck with it,
> > they really aren't invariants anymore.
> [...]
>
> > I suppose there might be a use for a shared_ptr::release() when
> > use_count() == 1.  Do you have something in mind?
>
>   Our use case is this: we have a chunk of code that stores shared
>   pointers in a queue.  We now pop these shared pointers off the queue
>   in function A and pass them into a 3rd party library that offers a
>   very C-ish interface; we send in void*'s (argh).  Later, in function
>   B, we get the void*'s back and can call delete on them.
>
>   The problem is that when we pop the shared pointers off the queue in
>   function A, they go out of scope at the end of the function call and
>   delete what they hold.
>
>   Now, we know for an absolute fact that in function A use_count() ==
>   1.  We can, of course, copy the underlying information out and work
>   with that, but that seems wasteful.  We'd like to get a pointer to
>   the data out of the shared pointer and relieve the shared pointer of
>   the responsibility for cleaning it up.
>
>   ... that is, more-or-less exactly what was described.
>
>   The solution we're pursuing right now is to change what we store in
>   the queue (e.g. don't store smart pointers).  Arguably we shouldn't
>   be using them anyway, in this case, but going back to raw pointers
>   instead really goes against the grain. :-)

For C++0X, and unique (sole) ownership patterns such as you describe, my
recommendation is to use std::unique_ptr:

void chunk()
{
   std::queue<std::unique_ptr<P>> q;
   q.push(std::unique_ptr<P>(new P));
   ...
}

void A(std::queue<std::unique_ptr<P>>& q)
{
   third_party_put(q.front().release());
   q.pop();
}

void B()
{
   std::unique_ptr<P> ptr(static_cast<P*>(third_party_get()));
   ...
}

unique_ptr is a non-copyable, move-only smart pointer which can safely
be put into C++0X standard containers.  Its semantics are similar to
auto_ptr except that one can not move from lvalues with copy syntax.
The overhead is identical to auto_ptr.

I believe its use in the pattern above will be simpler, safer and more
efficient than special casing shared_ptr when the use count is 1.
Making a special case of shared_ptr when the use count is 1 is something
that will need to be checked at run time and can possibly fail.  One can
think of unique_ptr as a shared_ptr where it is known (and enforced) at
compile time that the use count is no greater than 1.  If you accidently
code a situation where the use count threatens to grow above 1, the
compiler will let you know.

I realize this is not a solution you can code today.  However in
suggesting modifications to shared_ptr, we are implicitly talking about
C++0X anyway.

-Howard

---
[ 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, 15 Oct 2007 00:58:49 GMT
Raw View
* Carl Barron:
> "Alf P. Steinbach" <alfps@start.no> wrote:
>
>> In other words, the transferTo() member function seems to be safe and
>> useful functionality that can only reasonably be implemented by
>> shared_ptr itself, and is not currently present.
>>
>> I see no way to implement transferTo() using only the current public
>> functionality of shared_ptr.
>   If you create the shared_ptr then its easy

If you don't create it then it's not easy.  My article was about the
"not easy" case.  The easy case isn't much to remark about.


>, Use a custom deleter
> with a tunable action such as
>
>     struct may_be_delete
>     {
>         bool do_it;
>         may_be_delete():do_it(true){}
>         void operator () (T *p)
>         { if(do_it) delete p;}
>     };

Yes, I noted that elsewere in reply to Dave Steffen, and gave code using
about the same idea, only a bit more const correct both in operator()
etc. (const correctness for argument to operator() is important because
there are cases where you want to store T const* in a shared_ptr).


>     now get_deleter<may_be_delete>(p) will return a non const ptr
> to the deleter. allowing do_it to be changed to false. The result
> is when the shared_ptr's dtor is called nothing is done to the ptr
> it contains.
>    You just showed an example of why get_deleter<>() returns a non const
> ptr.:)

No, you have misunderstood.

Completely.

With a non-const get_deleter, and with you the creator of the
shared_ptr, the code above is unnecessary: all you need to do then is to
install a no-op deleter function.


>   Of course if the deleter is wrong or it is not unique then
> the copy approach is all there is.

I'm sorry, that's not meaningful to me.


Cheers, & hth..,

- Alf

--
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                      ]