8

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

Frank
  • 4,341
  • 8
  • 41
  • 57
  • 1
    You basically pass it a [function pointer](https://stackoverflow.com/questions/19053351/how-do-i-use-a-custom-deleter-with-a-stdunique-ptr-member) that has an empty body. – Cory Kramer Jun 25 '15 at 17:08
  • 7
    "custom deleter that does nothing": Why you need a `std::unique_ptr` then? – 101010 Jun 25 '15 at 17:08
  • 1
    You said that "the C++ side expects a unique_ptr"; is that in your control? Can you change the C++ side to *not* expect a `unique_ptr`? – Sam Estep Jun 25 '15 at 17:10
  • Not in my control - I have to have a std::unique_ptr, or I would already have removed it :-) Can I do something like declaring a lambda type inline at the point of definition of ptr? That's what I'm looking for. – Frank Jun 25 '15 at 17:13
  • @Frank No, you can't because lambdas can't be default constructed. – Shoe Jun 25 '15 at 17:13
  • @Jeffrey - Thanks! So I'll have to resort to more than 1 line of code to do that. Pity. – Frank Jun 25 '15 at 17:14
  • I'd consider making a copy of your object inside a unique_ptr and let your library handle the lifetime of its own copy of the object. – KABoissonneault Jun 25 '15 at 17:20
  • Wrap 'MyClass' in some 'YourClass' managing the lifetime –  Jun 25 '15 at 17:28
  • FWIW, stateless lambdas _can_ be default constructed in c++20, and implicitly convert to a function-pointer. Now a one-liner noop deleter could be `auto myPtr = std::unique_ptr(myClassRawPtr, [](MyClass*){});` – Chris Uzdavinis Oct 23 '21 at 21:25

3 Answers3

16

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.

lisyarus
  • 15,025
  • 3
  • 43
  • 68
  • 1
    If you need same type maybe then you can use std::shared_ptr without deletion – Gelldur Nov 01 '17 at 12:47
  • 1
    @lisyarus It is weird, I agree, but I've actually had a use case for this. For unit testing, I needed to mock an object that was held in a unique ptr. I was using the fakeit framework, which automatically creates mocked objects given only an ABC interface. Once a mocked object is created by fakeit, you can get a pointer to it. The problem was that fakeit manages the lifetime of mocked objects. The workaround, of course, was a unique ptr with a no-op deleter. – Loss Mentality Dec 11 '18 at 21:07
  • @LossMentality How exactly did you mock it? As mentioned in the answer, two `unique_ptr`'s with different deleter types are different types themselves. So, to properly mock this, you'd have to make your code dependent on the deleter type. – lisyarus Dec 11 '18 at 21:11
  • @lisyarus Wanted to discuss type in my first comment but ran out of room. The unique_ptr was a private class member. We have a macro defined when compiling unit tests; when macro was defined, the unique_ptr was a no-delete uniuqe-ptr that pointed to a mocked object; when not defined, it's a std::unique_ptr pointing to real object. This worked well in this case because, aside from the definition of the unique_ptr and a few ctor lines, the rest of the class only relied on the unique_ptr interface, and not the type of the unique_ptr (e.g. it wasn't passed to any functions)... – Loss Mentality Dec 12 '18 at 16:19
  • @lisyarus (continued) This solution would have been harder otherwise, with more #ifdefs needed, but we only needed a few. And while this solution removed a few lines of production code from test coverage, the ability to mock what this pointer pointed to brought the rest of the class into test coverage, which more than made up for it. – Loss Mentality Dec 12 '18 at 16:19
  • @lisyarus (continued) There might well be a more elegant solution or one that would be better if the type was needed in more places (I dunno, maybe a facade class template that could hold either pointer type), but anything like that would add complexity to the production code... – Loss Mentality Dec 12 '18 at 16:33
  • 1
    `std::unique_ptr` has the meaning of _uniqueness_ to it, Also, you cannot delete a `std::unique_ptr` by mistake. Both of these properties might be wanted without requiring the pointed to object to be deleted. – Fabio A. Dec 19 '19 at 17:04
  • This is useful in unit tests if you need to pass a unique_ptr, but actually want to use a static object to avoid having to re-initialize an expensive-to-create object many times. – jciloa Aug 05 '20 at 11:54
  • 2
    This is useful in embedded devices when trying to manage access to a global memory mapped resource that stays alive for the whole duration of the program. A unique_ptr with noop deleter in that case has access ownership semantics with modules being able to explicitly give and take ownership of access to the shared resource. – Liarokapis Alexandros Jul 19 '21 at 18:05
0

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.

Loss Mentality
  • 364
  • 4
  • 11
0

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

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