Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::unique_ptr test before taking ownership

I have a class which is basically a queue used to transfer dynamically allocated objects between 2 threads. The first thread creates the objects and the second one consumes them. I use std::unique_ptr to pass the objects ownership from thread 1 to thread 2.

Actually calls to the method that put the objects into the queue is like this:

queue.put(std::move(unique_ptr_to_my_object));

and the signature:

bool Queue::put(std::unique_ptr<T> p);

The problem is the put() method has to check some condition to decide if the object could be added to the queue. In case the condition is false the method simply returns false to indicate it can't add the object to the queue, but the object is destroyed because ownership has already be taken by put().

So I wanted to know if it's ok to rewrite put() like this or if there is a better solution:

bool Queue::put(std::unique_ptr<T> &ref) {
    if(CANNOT_ADD)
        return false; // ownership remains in the calling function
    std::unique_ptr<T> p = std::move(ref); // we know we can add so take ownership
    /* ... */
}
like image 907
James Magnus Avatar asked Jan 27 '23 14:01

James Magnus


2 Answers

Yes, that's fine. The alternative would be:

std::unique_ptr<T> Queue::put(std::unique_ptr<T> p) {
    if (CANNOT_ADD)
        return p; // Ownership returned to caller.
    /* ... */
    return {}; // Return empty nullptr to indicate success.
}

Your way has the advantage that the caller retains ownership if the code in ... throws.

like image 67
Martin Bonner supports Monica Avatar answered Feb 06 '23 14:02

Martin Bonner supports Monica


You can change signature of the function to:

std::unique_ptr<T> Queue::put(std::unique_ptr<T> p);

so if that function cannot take that object it would return that pointer back, or nullptr otherwise. Another solution is to take ownership conditionally:

bool Queue::put(std::unique_ptr<T> &&p);

and move out object only in success. Accepting rvalue reference vs lvalue one is better in this case due to at least 2 reasons:

  1. You can still pass temporary.
  2. You need to use std::move explicitly in calling code.

Though the fact that you can use that pointer after std::move make this variant is less readable.

like image 30
Slava Avatar answered Feb 06 '23 15:02

Slava