Question

I have a class provided from a library like so:

template <typename T>
class TypedClass
{
public:
    typedef typename boost::shared_ptr<TypedClass<T> > Ptr;

    T m_data;
    T* m_pointer_data;
};

Assuming I'm willing to accept that int and float are always the same size (and alignment) on this particular architecture, this seems valid to me:

TypedClass<int>* int_object = new TypedClass<int>();

TypedClass<float>* float_object = reinterpret_cast<TypedClass<float>* >(int_object);

Now I'm trying to achieve the same thing using boost shared_ptrs and have come up with this:

TypedClass<int>::Ptr int_object = TypedClass<int>::Ptr(new TypedClass<int>());

void* int_object_void_pointer = reinterpret_cast<void*>(int_object.get());

TypedClass<float>::Ptr float_object(reinterpret_cast<TypedClass<float>*>(int_object_void_pointer));

Which appears to work fine, but this use of shared pointers will cause the object to be deleted twice which I'd like to avoid.

Important to note that 'TypedClass' is part of a third-party library and that this library uses shared pointers for all it's internal functionality, so I need the data in this form. I have previously solved this problem inheriting from boost enable_shared_from_this, but that's not possible here.

This is a just a simple technique to attempt to reuse the same object for data types that have the same size without having to allocate a new object with the new type.

Suggestions welcome.

Was it helpful?

Solution

shared_ptr<T> has an interesting overloaded constructor:

template<class Y> shared_ptr(shared_ptr<Y> const & r, element_type * p);

Basically this constructs a shared_ptr which takes the deleter and the refcounting from r, except that it holds p.

You can use it like this:

TypedClass<int>::Ptr int_object = TypedClass<int>::Ptr(new TypedClass<int>());

TypedClass<float>::Ptr float_object(int_object,reinterpret_cast<TypedClass<float>*>(int_object.get()));

EDIT:

If you are using Boost >= 1.53.0, there is also boost::reinterpret_pointer_cast. So you can write:

TypedClass<float>::Ptr float_object = boost::reinterpret_pointer_cast<TypedClass<float> >(int_object);

OTHER TIPS

If you really attempt to reuse the same object for data types that have the same size without having to allocate a new object with the new type from third-party library, you have limited choices:

  1. You should not allocate shared_ptr from the raw pointer, otherwise it gets deleted twice, causing segment fault;
  2. You either re-use the type (shared_ptr) so that you can directly "copy" by operator=(); or cat the raw pointer and make sure you do NOT do changes that affect the memory allocation/deletion.

As your example, I suggest code like below:

float* float_ptr = reinterpret_cast<float*>(&int_object->m_data);
// Do something with *float_ptr
// And never delete it!

You could use boost pointer cast. This is a very ugly solution but at least the ref counting will work this way.

TypedClass<int>::Ptr int_object = TypedClass<int>::Ptr(new TypedClass<int>());
TypedClass<float>::Ptr float_object = boost::static_pointer_cast<TypedClass<float>>(boost::shared_ptr<void>(int_object));

I think you can't except if you overload the shared ptr class itself with two parameters typename, as it keeps the reference to the data and deletes when the count is 0. But as you have to go from a type to another, the boost shared ptr, will think that you released the data anyway.

shared_ptr p=ptr; //adds a ref if ptr and p are of the same type.

if the type is not the same you retrieve the inner data and then release it.

another solution may be to have all the data to hold in this container using boost::any.

If TypedClass is allocated on your code (and not in the external library), you can use specific destructor to prevent multiple destruction:

template<class T>
struct NullDestructor
{
  void operator()(TypedClass<T> *& t) { /* nothing to do */ }
};

template<class T>
typename TypedClass<T>::Ptr make_fake_shared_ptr( TypedClass<T> * ptr )
{
  return typename TypedClass<T>::Ptr(ptr, NullDestructor<T>() );
}


TypedClass<int>::Ptr int_object = make_fake_shared_ptr<int>(new TypedClass<int>());

TypedClass<float> * ptr = reinterpret_cast<TypedClass<float>*>(int_object.get());
TypedClass<float>::Ptr float_object = make_fake_shared_ptr<float>(ptr);

With this solution, you're in charge of destructing memory manually at the end:

delete float_object.get();

You can improve this solution by using a custom allocator and a pool.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top