[Programming C++] From shared_ptr to shared_ptr maintaining the use_count

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 POINT

c=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 POINT

a=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!
}
}

 

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: