No op delete for unique_ptr

6.3k views Asked by At

What would be the most concise way to pass unique_ptr a custom deleter that does nothing? I need for a JNI function I'm writing, where the C++ side expects a unique_ptr, BUT, I don't want the object held by the unique_ptr to be deleted upon exiting the JNI function - I take care of the deletion later. So I'd like to do something like:

std::unique_ptr<MyClass, noop_delete> ptr;

In one line - not with a separate function definition :-)

3

There are 3 answers

9
lisyarus On BEST ANSWER

As @101010 pointed out, that's very strange to have a std::unique_ptr with a nop deleter, since the only valuable thing std::unique_ptr has is actually the deleter. Also, you said that "C++ side expects a unique_ptr", but a std::unique_ptr with a different deleter would be a different type, and this may not work.

Nevertheless, here's the way to do it:

struct nop
{
    template <typename T>
    void operator() (T const &) const noexcept { }
};

template <typename T>
using nop_unique_ptr = std::unique_ptr<T, nop>;

Note that this nop type can be used as no-operation anywhere in place of a one-argument functor.

0
Loss Mentality On

My answer to @lisyarus's question in the comments to his answer prompted me to come up with a better solution than the one I gave there. This deals with the fact already stated by @lisyarus: a no-op deleter unique_ptr is of a different type than a unique_ptr with a delete deleter.

I'm posting this as a separate answer because it may be relevant to others (besides, this wouldn't fit in a single comment).

Context: For unit testing, the FakeIt mocking framework manages the lifetime of mock objects, so when an object pointed to via unique_ptr needs to be mocked, we need a unique_ptr with a no-op deleter.

// As in @lisyarus's answer...
struct nop
{
    template <typename T>
    void operator() (T const &) const noexcept { }
};

// NOTE: We have no use for a pointer that doesn't delete unless we're mocking, so 
// the types below are named to indicate that.

#ifndef WE_ARE_BUILDING_UNIT_TESTS
// Production build - we want a unique_ptr that deletes.
template <typename T>
using mockable_unique_ptr = std::unique_ptr<T>;
#else
// Unit test build - we want unique_ptr that doesn't delete.
template <typename T>
using mockable_unique_ptr = std::unique_ptr<T, nop>;
#endif

Now, mockable_unique_ptr will switch types automatically based on the build type, meaning you don't have to sprinkle #ifdefs all over your code. Of course, there will be certain locations that you'll need to #ifdef/#else and use slightly different code for unit test builds (likely at the site of pointer initialization, but if your mock is also created there, you'd need to do that anyway). The rest of the code gets to remain the same, though, as the unique_ptr's interface doesn't change.

0
John O'Donnell On

how about std::unique_ptr::release? as in

void JNIfunc (T*) {};
std::make_unique( new T) t;
JNIfunc( t.release);