I have an RAII wrapper around the cnats
library. We don't need to understand the details of cnats
. There are just two important piecies. cnats
is the C client for communicating with a NATS message broker. As is usually the case, they have custom creation and deletion functions for all of their structures.
I have an RAII wrapper for creating a natsMsg
object. The constructor:
NatsMessage(const std::string& subject, const std::string& data) {
natsMsg* temp;
HANDLE_STATUS(natsMsg_Create(&temp, subject.c_str(), nullptr, data.c_str(), data.size()));
msg = std::shared_ptr<natsMsg>(temp, [](natsMsg* p) {natsMsg_Destroy(p);});
}
There are two publish functions: js_PublishMsg
and js_PublishMsgAsync
. The regular function just publishes the message. I am presently adding support for the Async version which takes over the message and calls the natsMsg_Destroy
when it is done causing a race condition and a double free error.
From reading about std::shared_ptr
:
The ownership of an object can only be shared with another shared_ptr by copy constructing or copy assigning its value to another shared_ptr.
There is similar language for std::unique_ptr
.
In essence my problem is that I would like to use a smart_pointer with a custom deleter. When I call the Async publish function, I would like to somehow "remove" ownership so that the c library handles the deletion.
I'm okay switching to a unique_ptr
or other smart pointer (or some other way of accomplishing this).
if you don't need shared ownership, you can use std::unique_ptr
which has release()
function
struct NatsMessage{
NatsMessage() {
natsMsg* temp;
GetMsg(&temp);
msg.reset(temp);
// when you need to transfer ownership to C library
// natsMsg* m = msg.release();
}
std::unique_ptr<natsMsg,decltype(&natsMsg_Destroy)> msg = {nullptr, &natsMsg_Destroy};
};
if you want to keep using std::shared_ptr
, you can set some flag to disable the deleter.
also you can use raw pointer and manually release it in destructor, note this also require correctly handle copy/move case.