convert shared_ptr to auto_ptr?
Asked Answered
P

5

6

I need to obtain auto_ptr from shared_ptr in my code. I can do reverse operation - convert auto_ptr to shared_ptr as shared_ptr has such constructor:

template<class Y> explicit shared_ptr(std::auto_ptr<Y> & r);

Can I convert shared_ptr to auto_ptr? Or it is impossible by design?

Phoebe answered 23/1, 2011 at 11:27 Comment(0)
D
4

A shared pointer can be shared by many things, you can't just take it from them all somehow. This is elaborated by Artyom and peoro.

One approach is to make a temporary auto_ptr, and release it from handling the pointer at the end of the scope. dalle outlines a first approach, but this suffers from lack of exception-safety (might accidentally delete), and it cannot protect you from accidentally passing it to a function that's going to transfer ownership (where the delete falls out of our hands).

We can make our own wrapper to avoid this, though:

template <typename T>
class auto_ptr_facade
{
public:   
    auto_ptr_facade(shared_ptr<T> ptr) :
    mPtr(ptr),
    mAuto(ptr.get())
    {}

    ~auto_ptr_facade()
    {
        // doesn't actually have ownership
        mAuto.release();
    }

    // only expose as const, cannot be transferred
    const auto_ptr<T>& get() const
    {
         return mAuto;
    }

    operator const auto_ptr<T>&() const
    {
         return get();
    }

private:
    auto_ptr_facade(const auto_ptr_facade&);
    auto_ptr_facade& operator=(const auto_ptr_facade&);

    shared_ptr<T> mPtr;
    auto_ptr<T> mAuto;
};

Now you can treat a shared_ptr like a const auto_ptr, in a scope:

template <typename T>
void foo(shared_ptr<T> ptr)
{
    auto_ptr_facade<T> a(ptr);

    // use a
}
Dyke answered 23/1, 2011 at 11:39 Comment(5)
@GMan: but, if it expires then the pointee is already deleted by ptr.reset() and result becomes a dangling pointerKopple
Also in almost any useful case ptr will be a copy of the shared_ptr passed as a parameter: try_make_auto_ptr(my_local_ptr), so it won't expire.Kopple
@ybungalobill: Oh, duh, haha. Sleepy. :) I've expanded on dalle's answer instead.Dyke
-1 In the current code ptr is passed by value. That nearly ensures that it isn't the only ref. Also, the code is needlessly inefficient by using a weak_ptr. Methinks original authors were Java-men. shared_ptr::use_count exists for both boost::shared_ptr and C++0x std::shared_ptr. Finally, the reset would tend to destroy the referred to object. Uh oh...Glacial
@Alf: There's a timer. No worries, I see a typo.Dyke
L
9

It is impossible by design as the object may be shared with other shared pointer and thus "fetching" it to auto_ptr may lead to deleting referenced object.

For same reason shared_ptr has no "release" member function as auto_ptr.

Edit:

Even if shared_ptr had some kind of "release" method or allowed to remove its reference without destroying the object it would not work for following case (threads A, B):

A: { 
A:     int count = sp.use_count();
  Context Switch
B: shared_ptr<bar> my_sp = weak_sp.lock();
B: // now use_count = 2 but A thinks it is 1
  Context Switch
A:     auto_ptr<bar> ap;
A:     if(count == 1) 
A:      ap.reset(sp.release()); 
A:      // actutally there is no sp.release but what if
A:      ap->foo();
A: }  // delete the object pointer by ap as it goes out of scope
  Context Switch
B: my_sp->foo(); // Ooops - object is deleted!
Loosestrife answered 23/1, 2011 at 11:31 Comment(10)
shared_ptr's "unique" method could in principle tell you whether the shared_ptr has the only remaining reference to the object, in which case transfer to an auto_ptr would seem to be a bit more of a reasonable thing to do. But without any supporting API (ie a release method) there's no way. (And in my experience, shared_ptr code which makes use of unique or count is usually doing something wrong).Acea
@Acea The problem with unique that it can cause race condition when weak_ptr is used. For example if current count is 1 but in other thread some user of weak pointer creates shared_ptr it may become 2 and your previous check would be invalid.Loosestrife
@Alf P. Steinbach Yes it is correct because of danger of conversion of weak_ptr to shared_ptr after checking the "uniquness" and before the actual release.Loosestrife
@Artyom: you say it's impossible, I've showed it is possible in a certain case. "Impossible" != "possible". Your answer is just plain wrong, sorry.Glacial
@Alf: There are different shared_ptr implementations. The answer is vague, at worse, not wrong. It is indeed impossible in C++0x, for example.Dyke
@Alf P. Steinbach you can even cast the pointer to other structure and reset is use count to 0 or do other 101 things. This doesn't changes the fact that it is not what shared_ptr is designed for and if you want to cast shared_ptr to auto_ptr then maybe you are doing something wrong. Also your "possible" does not work in general case - so it is impossible as in general case there is no solution.Loosestrife
@Artyom: Yes, it's stuff like this which leads me to think "bad code smell!" if I see unique()/count() used. IMHO they're there to support debugging and that's all. I guess a shared_ptr providing some sort of atomic "release_if_unique" method could add appropriate locking internally... but that's a whole new thing.Acea
@Gman: No, it's not different in C++0x. C++0x std::shared_ptr has both use_count and get_deleter. Works fine. :-)Glacial
@Alf I do like shared_ptr design, but it does not makes what you suggest less dangerous or applicable in generic case. So if you want to use the technique you suggested you need to be super careful and understand in 1000% how shared_ptr works. So I'm sorry but I still think your anwer is wrong.Loosestrife
@Artyom: your answer is incorrect. your dislike of shared_ptr design does not make my answer wrong. your answer, on the other hand, saying that that is impossible, is technically wrong. don't you understand that "impossible" != "possible"?Glacial
D
4

A shared pointer can be shared by many things, you can't just take it from them all somehow. This is elaborated by Artyom and peoro.

One approach is to make a temporary auto_ptr, and release it from handling the pointer at the end of the scope. dalle outlines a first approach, but this suffers from lack of exception-safety (might accidentally delete), and it cannot protect you from accidentally passing it to a function that's going to transfer ownership (where the delete falls out of our hands).

We can make our own wrapper to avoid this, though:

template <typename T>
class auto_ptr_facade
{
public:   
    auto_ptr_facade(shared_ptr<T> ptr) :
    mPtr(ptr),
    mAuto(ptr.get())
    {}

    ~auto_ptr_facade()
    {
        // doesn't actually have ownership
        mAuto.release();
    }

    // only expose as const, cannot be transferred
    const auto_ptr<T>& get() const
    {
         return mAuto;
    }

    operator const auto_ptr<T>&() const
    {
         return get();
    }

private:
    auto_ptr_facade(const auto_ptr_facade&);
    auto_ptr_facade& operator=(const auto_ptr_facade&);

    shared_ptr<T> mPtr;
    auto_ptr<T> mAuto;
};

Now you can treat a shared_ptr like a const auto_ptr, in a scope:

template <typename T>
void foo(shared_ptr<T> ptr)
{
    auto_ptr_facade<T> a(ptr);

    // use a
}
Dyke answered 23/1, 2011 at 11:39 Comment(5)
@GMan: but, if it expires then the pointee is already deleted by ptr.reset() and result becomes a dangling pointerKopple
Also in almost any useful case ptr will be a copy of the shared_ptr passed as a parameter: try_make_auto_ptr(my_local_ptr), so it won't expire.Kopple
@ybungalobill: Oh, duh, haha. Sleepy. :) I've expanded on dalle's answer instead.Dyke
-1 In the current code ptr is passed by value. That nearly ensures that it isn't the only ref. Also, the code is needlessly inefficient by using a weak_ptr. Methinks original authors were Java-men. shared_ptr::use_count exists for both boost::shared_ptr and C++0x std::shared_ptr. Finally, the reset would tend to destroy the referred to object. Uh oh...Glacial
@Alf: There's a timer. No worries, I see a typo.Dyke
M
3

Usually it's a bad idea, since both auto_ptr and shared_ptr take the ownership of your pointer (they'll care about destroying it and stuff, according to different policies).

Having two different objects holding the ownership for the same pointer will likely result in run time errors unless your doing it for some really good (and weird!) reasons.

Muffin answered 23/1, 2011 at 11:31 Comment(0)
G
2

Assuming you want to transfer ownership from a shared_ptr to auto_ptr, this is only possible when

  • The reference count of the shared_ptr is 1, and
  • the shared_ptr was originally created with a custom deleter function, and
  • you know the type of that deleter function.

Given that, here's how:

#include <iostream>
#include <boost/shared_ptr.hpp>     // boost::shared_ptr
#include <memory>                   // std::auto_ptr

typedef boost::shared_ptr<int>  IntSharedPtr;
typedef std::auto_ptr<int>      IntAutoPtr;

template< class Type >
void myCustomDeleter( Type* p )
{
    delete p;
}

IntSharedPtr newSharedInt()
{
    return IntSharedPtr( new int( 42 ), &myCustomDeleter<int> );
}

IntAutoPtr makeAutoFrom( IntSharedPtr& sp )
{
    struct Dummy
    {
        static void deleter( int* ) {}
    };

    typedef void (*DeleterFunc)( int* );

    if( sp.use_count() > 1 ) { return IntAutoPtr( 0 ); }
    DeleterFunc*    d   = boost::get_deleter<DeleterFunc>( sp );

    if( d == 0 ) { return IntAutoPtr( 0 ); }

    int* const  p   = sp.get();
    *d = &Dummy::deleter;
    sp.reset();
    return IntAutoPtr( p );
}

template< class T >
T& refTo( T const& r ) { return const_cast< T& >( r ); }

int main()
{
    IntAutoPtr  p( makeAutoFrom( refTo( newSharedInt() ) ) );

    std::cout << (p.get() == 0? "Failed" : "Worked" ) << std::endl;
}

Note: this technique isn't thread-safe.

Cheers & hth.,

Glacial answered 23/1, 2011 at 12:44 Comment(9)
-1 It is not thread safe and it is very dangerous and what is even more important it can't become thread-safe. I would strongly discourage from using such "tricks".Loosestrife
The author explicitly asks if this is done by design, and yes, it is done by design. Almost every thing can be worked around, but this does not make this method reasonable.Loosestrife
Also shared_ptr designed to be thread safe and this access violates its property.Loosestrife
You "solution" does not work in general case: it is not thread safe and it does not work with general deleter - so it is plain wrong :-)Loosestrife
@Artyom: i've specified exactly the conditions for that code. the functionality is there by design. it proves your answer incorrect. stop bickering.Glacial
Please add at least at the beginning of pre-conditions also: "it is not used from multiple threads or no weak_ptr is used"Loosestrife
@Artyom: the bit about weak_ptr would be wrong, the bit about threads is already addressed (do read). just to repeat: your answer that the above is impossible, is incorrect. is that so hard to understand -- something that is possible, is not impossible? please, stop now.Glacial
@Alf - I don't want to continue, but given general case it is impossible, given very specific case you suggest it seems to be possible, so the answer possible or impossible is conditional, yet in the general case the answer is impossible.Loosestrife
@Artyom: please don't shout. your answer is incorrect: you say the above is impossible, while it is not only possible but a possibility that is designed-in. you do not mention anything about "general case" in your answer. if you had so qualified your statement, then it could have been correct. you can, in principle, learn from that.Glacial
P
1

You should not do that, as auto_ptr takes ownership of the pointer.

But you can do it, but make sure you call release before you fall out of scope.

void foo(shared_ptr<Y> s)
{
    auto_ptr<Y> a(s.get());

    // use a

    a.release();
}

EDIT: The above solution is not exception safe. The following should work, combining a guard class with the guarantee that a const auto_ptr cannot be copied:

void bar(const auto_ptr<Y>& p);

struct as_const_auto_ptr
{
    explicit as_const_auto_ptr(const shared_ptr<Y>& p) : p(p), a(p.get()) {}
    ~as_const_auto_ptr() {a.release();}
    operator const auto_ptr<Y>&() {return a;}
    const shared_ptr<Y> p;
    auto_ptr<Y> a;
};

void foo(shared_ptr<Y> s)
{
    as_const_auto_ptr a(s);

    // use a.
    bar(a);
}
Paulenepauletta answered 23/1, 2011 at 11:32 Comment(7)
And if an exception is thrown during // use a?Dyke
@ybungalobill: And if I accidentally pass it to a function by-value? :) This solution is just too easy to mess up.Dyke
@GMan: It is not exception safe, but as @ybungalobill notes, use a guard object.Paulenepauletta
@GMan: well, reverting my vote. Although it does what is asked in the question it's surely useless!Kopple
@GMan: But as I noted, you should not do it, but still it is possible.Paulenepauletta
Didn't mean to step on your toes with the solution.Dyke
@GMan: No worries, you were correct in your observations, and faster giving the correct answer.Paulenepauletta

© 2022 - 2024 — McMap. All rights reserved.