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.