Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mix C++ shared pointers and object references

I have the following code that uses a C++ object of class Datum within an Objective-C function work:

void work(const Datum &datum) {
    dispatch_async(dispatch_get_main_queue(), ^{
        // Work with datum.
    });
}

This code is called with an instance that is actually a boost::shared_ptr<Datum>, i.e.

boost::shared_ptr<Datum> the_datum(new Datum());
work(*the_datum);

In this situation it is possible that the instance the_datum gets deallocated before the block is run inside work (the call to dispatch_async performs an asynchronous operation on datum that is executed later; the call and thus the function work return immediately). This obviously results in a disaster.

One solution might be to not pass a reference to work, but a boost::shared_ptr<Datum>, instead. But there may be situations when references are preferred, see e.g. this thread. Is there any way to keep the interface of work (i.e., pass datum as a reference), but still prevent the deallocation of the shared pointer prior to the completion of the block?

like image 909
user8472 Avatar asked Sep 18 '13 16:09

user8472


1 Answers

There is no way to accomplish this by leaving the interface to work() the same, and pass in a reference to datum. There is no way to use the datum to prevent the reference count from decrementing. Consider the following buggy program:

#include <memory>
int main () {
    std::shared_ptr<int> p1(new int);
    int &x = *p1;
    std::shared_ptr<int> p2(&x);
}

This code crashes with a double free, because the shared_ptr<> control structure does not follow the pointer to the object, but is followed through shared_ptr<> itself.

What you can do is change work() to take a shared_ptr(), but add some more code to the block passed to dispatch_async() so that it can use a reference within that code. Since you are transferring ownership to the asynchronous routine, you should use unique_ptr<> instead. I know zilch about Objective-C, so this syntax might be wrong:

void work(std::unique_ptr<Datum> &datumptr_ref) {
    __block std::unique_ptr<Datum> datumptr(std::move(datumptr_ref));
    dispatch_async(dispatch_get_main_queue(), ^{
        Datum &datum = *datumptr
        // Work with datum.
    });
}
like image 99
jxh Avatar answered Oct 01 '22 21:10

jxh