[Programming C++] From shared_ptr to shared_ptr maintaining the use_count
agosto 3, 2014 Deja un comentario
Sometimes we need to store shared data pointers in an anoymous shared pointer shared_ptr<void>. The problem is when you want to convert back the shared_ptr<void> to shared_ptr<T> again. A priori, it is not possible, but we can do some tricks to solve this issue. Neddless to say that the first solution is – of course – NOT USE shared_ptr<void>. Try to use boost::any or boost::variant objects in your problem. But still, if there may be some strange cases where it can be interesting the use of anonymous shared_ptrs
The most you can until the momment is getting the original data is using the shared_ptr<void>::get() method to get the void* pointer. However you must maintain the shared_ptr<void> alive if you want to avoid the segmentation fault. The following test would FAIL in the last line:
TEST(basic_test, original_from_shared_void_to_t)
{
std::shared_ptr<A> a(new A);
std::shared_ptr<void> b =a;ASSERT_TRUE(a.use_count()==2);
ASSERT_TRUE(b.use_count()==2);
a=std::shared_ptr<A>();
ASSERT_TRUE(a.use_count()==0);
ASSERT_TRUE(b.use_count()==1);
printf(«Testing access memory…\n»);
A* data=(A*)b.get();
data->foo(); //you can access to the fields and methods
//OK by the moment..b=std::shared_ptr<void>();
ASSERT_TRUE(b.use_count()==0);
//and now crash because memory has been freed…
data->foo(); //segmentation fault
}
Do you have any idea? Perhaps you are thinking in this one (but it does not work):
std::shared_ptr<A> data= std::shared_ptr<A>((A*)b.get());
data->foo(); //it works because b still references it//but the problem is the next fact
ASSERT_TRUE(b.use_count()==1);
ASSERT_TRUE(data.use_count()==1);
//both have different counts, they both should be 2 instead 1
//so here comes the crash…b=std::shared_ptr<void>();
//the destructor is called
data->foo();
lesson lernt: you have to maintain b (or a copy of it) alive while the new pointer «data» is alive.
How to do it without think about it continuing working as always? The solution is the «__magic_reinterpret_pointer_cast». see the code:
template<typename T>
std::shared_ptr<T> __magic_reinterpret_pointer_cast(std::shared_ptr<void> b)
{
return std::shared_ptr<T>((T*)b.get(),[b](T* x){});
}
Whit it you can go back to your type shared_ptr<A> from shared_ptr<void> avoiding any segmentation fault. The trick is store a reference of the shared_ptr<void> inside the new shared_ptr<A>. Observe that there will be different «use counts» groups, so the use_count method is not reliable anymore. Appart from this everything look work well: See this example and please make me know other limitations on the approach if you notice it.
NOTE: Be careful because it has the same limitations to the reinterpet_cast. ¡¡This is not valid to cast to base or derived types!!. TEST(basic_test,t1)
TEST(basic_test, original_from_shared_void_to_t_reintrepret_cast)
{
std::shared_ptr<A> a(new A);
std::shared_ptr<void> b=a;ASSERT_TRUE(a.use_count()==2);
ASSERT_TRUE(b.use_count()==2);//removing one instance
b=std::shared_ptr<void>();
ASSERT_TRUE(a.use_count()==1);
ASSERT_TRUE(b.use_count()==0);//restoring it again
b=a;ASSERT_TRUE(a.use_count()==2);
ASSERT_TRUE(b.use_count()==2);
{
//»make magic trick
std::shared_ptr<A> c=__magic_reinterpret_pointer_cast<A>(b);ASSERT_TRUE(a.use_count()==3);
ASSERT_TRUE(b.use_count()==3);
ASSERT_TRUE(c.use_count()==1); // THIS IS THE WARNING POINTc=std::shared_ptr<A>();
ASSERT_TRUE(a.use_count()==2);
ASSERT_TRUE(b.use_count()==2);
}{
//»make magic trick
std::shared_ptr<A> c=__magic_reinterpret_pointer_cast<A>(b);ASSERT_TRUE(a.use_count()==3);
ASSERT_TRUE(b.use_count()==3);
ASSERT_TRUE(c.use_count()==1); // THIS IS THE WARNING POINTa=std::shared_ptr<A>();
b=std::shared_ptr<void>();ASSERT_TRUE(a.use_count()==0);
ASSERT_TRUE(b.use_count()==0);
ASSERT_TRUE(c.use_count()==1); // VOILA, STILL 1!
}
}
Recent Comments