1

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?

ks1322
  • 33,961
  • 14
  • 109
  • 164
  • Does `c_lib_function` allocate memory and writes it's address in `msg`? If it does, then you probably should use it as a constructor. – lisyarus Feb 28 '14 at 12:31
  • Does `LDAPMessageDeleter` call the `c_lib_deleter` on `*msg_ptr`, and then `delete` `msg_ptr.get()` ? – Jarod42 Feb 28 '14 at 12:40
  • That code compiles? Does it at least give a warning? Because you're giving it a `LDAPMessage*` when it expects an `LDAPMessage**`... – Mooing Duck Mar 03 '14 at 18:36

3 Answers3

3

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

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • I just tested your snippet out and it does work! Any idea how I would get it working without having the pointer outside of a smart ptr for a short time? Is what I'm trying to do good style or just a waste of time? –  Feb 28 '14 at 12:40
  • 1
    @RodrigoSalazar If the `get` function would have returned a reference then it would have been possible, but no there is really no other way. But it shouldn't be any problem, unless you have lots of code between the function call and the `unique_ptr` creation. – Some programmer dude Feb 28 '14 at 12:44
  • 4
    @RodrigoSalazar I'd say the best way would be to create your own function which will contain just those 3 lines (pointer declaration, `c_lib_function()` call, `return unique_ptr`). – Angew is no longer proud of SO Feb 28 '14 at 12:48
  • 1
    @RodrigoSalazar A factory will also save your poor fingers from having to type `std::unique_ptr` over and over. – Casey Feb 28 '14 at 15:51
2

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.

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
1

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

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421