RAII when using ascyc c library

72 Views Asked by At

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

1

There are 1 best solutions below

0
apple apple On BEST ANSWER

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.