gfvalvo:
I need to think about that some. In my application if xQueueSendToBack fails, I want to immediately de-allocate the resource being pointed to.
In that case it's best to pass the unique pointer by value:
template <class T> BaseType_t myQueueSendToBack(QueueHandle_t xQueue,
std::unique_ptr<T> uptr, // by value
TickType_t xTicksToWait) {
auto ptr = uptr.get(); // gets the pointer without releasing ownership
auto res = xQueueSendToBack(xQueue, static_cast<const void *>(&ptr), xTicksToWait);
if (res == pdTRUE)
uptr.release(); // releases ownership without deallocating
return res;
} // resource pointed to by uptr is deallocated if ownership was not released
gfvalvo:
But, doesn't casting / sending by rvalue and receiving by rvalue-reference invoke move semantics? So, upon return from myQueueSendToBack(), won't the caller's unique_ptr be pointing to nothing?
No, under the hood, passing by rvalue reference is the same as passing by lvalue reference or by pointer. The different semantics for rvalues come from the ability to overload functions, i.e. creating one function that accepts lvalues and another function that accepts rvalues.
For example:
struct SomeContainer {
std::vector<int> vec;
void setValue(const std::vector<int> &vec) {
this->vec = vec; // invokes copy assignment of the std::vector class
}
void setValue(std::vector<int> &&vec) {
this->vec = std::move(vec); // invokes move assignment of the std::vector class, the
// actual "move" of the data happens here,
// no data is copied
}
};
std::vector<int> v{1, 2, 3, 4}; // expensive to copy
SomeContainer c{};
c.setValue(v); // v is an lvalue, so the "const &" overload is selected, and contents are copied
c.setValue(std::move(v)); // std::move(v) is an xvalue, so the "&&" overload is selected
The actual moving of the storage of the vector doesn't happen when calling std::move nor at the instant of calling setValue, it happens inside of the setValue function, because of the move assignment. If you were to remove the line "this->vec = std::move(vec);" from the setValue function, nothing would be moved, and v could still be used at the call site after calling setValue.
The distinction between lvalue references and rvalue references is mainly there so you can differentiate between values that you're still using and values that you no longer need and whose resources can be reused.
The callee accepts an rvalue reference to signal to the caller that it will claim ownership of anything you pass to it, so it might steal its resources to reuse them.
The caller passes an rvalue so the correct rvalue reference overload is selected, and to indicate that it wants an overload that might claim overship of the argument if the callee wants to. This is done by either passing a temporary (prvalue), or by calling std::move on an lvalue (xvalue).
westfw:
PieterP explains some C++, and simultaneously demonstrates why people still use C-style malloc()
Sigh.
I only go into depth because I know gfvalvo has enough C++ experience.
The problem here is not that memory safety in C++ is harder than memory safety in C, quite the opposite is true. The problem is that there is no such thing as memory safety in C, so when using C APIs, you have to be careful and you might have to write some nontrivial wrappers.
Once you wrap such an API correctly (or rewrite it in C++ with ownership in mind), it is easier to use and harder to misuse than the C API. Then the user doesn't have to care about who's responsible for cleaning up resources, whether to do a deep or a shallow copy, etc.
IMHO, the solution is to promote memory- and type-safe APIs, not fall back to unsafe C practices.