2

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).

genpfault
  • 51,148
  • 11
  • 85
  • 139
John Sallay
  • 241
  • 3
  • 8
  • `unique_ptr` has a release function. you can also set a flag in the deleter. – apple apple May 09 '23 at 17:19
  • Although imo there is no much reason to use smart pointer here anyway. The destructor can simply handle it. – apple apple May 09 '23 at 17:19
  • That's what I was looking for. It you change it to an answer I'll accept it. I didn't use the constructor/destructor to manage it because I was thinking of a shared ownership model. It would probably be fine as long as I delete the copy constructor/assignment operator. – John Sallay May 09 '23 at 17:47

1 Answers1

1

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.

apple apple
  • 10,292
  • 2
  • 16
  • 36