Вопрос

I'm in c++ and i'm writing a wrapper around a c library. The c library function i'm working with takes a type 'LDAPMessage **' as a parameter, where it will allocate the memory for you within the function. Normal usage looks like this:

LDAPMessage * msg;
c_lib_function(&msg); // allocates the memory for me

Now I want to use unique_ptr in this case, with a custom deleter. Assume my deleter 'LDAPMessageDeleter' exists and works in the following.

unique_ptr<LDAPMessage*, LDAPMessageDeleter> msg_ptr(new LDAPMessage*);
c_lib_function(msg_ptr.get());

Though this compiles for me, i'm getting a seg fault. Could this code be at fault here? Is this correct usage of unique_ptr?

Это было полезно?

Решение

It might be easier to do e.g.

LDAPMessage* msg;
c_lib_function(&msg);

// Now we have out pointer (hopefully)
std::unique_ptr<LDAPMessage, LDAPMessageDeleter> msg_ptr(msg);

Of course, the LDAPMessageDeleter have to call the proper function to free the memory (e.g. free is the memory was allocated with malloc).


The problem with the code you show in the question, is that you try to create a pointer to a pointer, but don't make that first-level pointer actually point anywhere.

What you are effectively doing in your code is this:

LDAPMessage** msg;
c_lib_function(msg);

What's happening in the C library, is that the pointer is passed "by reference". Since C doesn't actually have proper references, it's kind of emulated by passing pointers. And passing a "reference" to a pointer is done by using the address-operator to pass the address of the pointer (which then becomes a pointer to the pointer).

Другие советы

Someone in the SO chat developed code valuely like this that I can no longer find:

//ptrptr magic
#include <memory>

template<class T, class deleter>
class ptrptr_type {
public:
    ptrptr_type(std::unique_ptr<T, deleter>& ptr) 
        : uptr(&ptr), tptr(ptr.get()) {}
    ~ptrptr_type() {if (uptr->get()!=tptr) uptr->reset(tptr);}
    operator T**() {return &tptr;}
private:
    std::unique_ptr<T, deleter>* uptr;
    T* tptr;
};
template<class T, class D>
ptrptr_type<T,D> ptrptr(std::unique_ptr<T, D>& ptr) { return {ptr};}

And the usage of this ptrptr is quite simple:

std::unique_ptr<LDAPMessage, LDAPMessageDeleter> my_ptr; //can be null or initialized, whichever
c_lib_function(ptrptr(my_ptr)); //use ptrptr(my_ptr) whenever you need a T**

That's it. Real simple. http://coliru.stacked-crooked.com/a/644ed001ffd547ae

What happens is ptrptr(my_ptr) creates a temporary ptrptr_type on the stack, which takes the pointer from the unique_ptr. The ptrptr then exposes a pointer to it's internal pointer, which the C function can freely modify. Afterwards, the temporary ptrptr_type is destroyed, and its destructor puts the new value for the pointer back into the original unique_ptr.

This isn't correct usage of the pointer-to-pointer. The function takes a pointer to uninitialized pointer and fills it. In your first example, this is a local variable. If you want it to be managed by unique_ptr, it should still be a local, just contained within the unique_ptr object.

LDAPMessage * msg;
c_lib_function(&msg); // allocates the memory for me
std::unique_ptr< LDAPMessage, LDAPMessageDeleter > // This type contains a ptr
               msg_ptr( msg ); // transfer ownership to C++

LDAPMessageDeleter should receive a LDAPMessage *, and pass it to the library to be released if necessary, otherwise pass to free().

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top