I have read through many questions on SO on custom deleter for shared_ptr and unique_ptr, and the difference between the two. But, I still haven\'t
The other answer provided here was that something close to what I asked could be done through function returns of unique_ptr with custom deleter, which can be implicitly converted to a shared_ptr.
The answer given was that a deleter defined as a type trait was not possible for std::shared_ptr. The answer suggested as an alternative, to use a function which returns a unique_ptr, implicitly converted to a shared_ptr.
Since this isn't part of the type, it is possible to make a simple mistake, leading to memory leaks. Which is what I wanted to avoid.
For example:
// Correct usage:
shared_ptr s(createSurface(IMG_Load("image.png")));
// Memory Leak:
shared_ptr s(IMG_Load("image.png"));
The concept I want to express is having the deleter as part of the type (which unique_ptr allows), but with the functionality of a shared_ptr. My suggested solution is deriving from shared_ptr, and providing the deleter type as a template argument. This takes up no additional memory, and works in the same way as for unique_ptr.
template>
struct shared_ptr_with_deleter : public std::shared_ptr
{
explicit shared_ptr_with_deleter(T* t = nullptr)
: std::shared_ptr(t, D()) {}
// Reset function, as it also needs to properly set the deleter.
void reset(T* t = nullptr) { std::shared_ptr::reset(t, D()); }
};
Together with a deleter class (Thanks Jonathan Wakely. Way cleaner than my macro (now removed)):
struct SDL_Deleter
{
void operator()(SDL_Surface* p) const { if (p) SDL_FreeSurface(p); }
void operator()(SDL_RWops* p) const { if (p) SDL_RWclose(p); }
};
using SurfacePtr = std::unique_ptr;
using SurfaceShPtr = shared_ptr_with_deleter;
using RWopsPtr = std::unique_ptr;
using RWopsShPtr = shared_ptr_with_deleter;
Instances with SurfaceShPtr members are type guaranteed to clean up properly, the same as for SurfacePtr, which is what I wanted.
// Correct Usage (much harder to use incorrectly now):
SurfaceShPtr s(IMG_Load("image.png"));
// Still correct usage
s.reset(IMG_Load("other.png"));
I'll leave this up for a while, for comments, etc, without accepting the answer. Maybe there are even more dangerous caveats I've missed (having a non-virtual destructor not being one, as the parent shared_ptr is given charge of the deletion).