Problems Using Member Function as Custom Deleter with Std::Shared_Ptr

Problems using member function as custom deleter with std::shared_ptr

std::shared_ptr<SDL_Surface>(SDL_LoadBMP(....), [=](SDL_Surface* surface)
{
std::cout << "Deleting surface\n";
SDL_FreeSurface(surface);
});

or

void DeleteSurface(SDL_Surface* surface)
{
std::cout << "Deleting surface\n";
SDL_FreeSurface(surface);
}

std::shared_ptr<SDL_Surface>(SDL_LoadBMP(....), DeleteSurface);

EDIT:

Seeing your updated question, DeleteSurface should be a non-member function, otherwise you need to use std::bind or std::mem_fn or some other member function pointer adapter.

Calling custom deleter class member function for shared pointer

FileDeleter("tmpfile.txt") creates a deleter object to pass to the shared pointer.

The shared pointer's implementation stores a copy this somewhere as a variable, as well as the managed object, along the lines of

std::ofstream * object;
FileDeleter deleter;

The shared pointer's destructor will invoke this as

deleter(object);

which will call the deleter's overloaded operator(), deleting the object and removing the file.

Custom deleter for shared_ptr giving 'no context error'

First off you have a couple typos/syntax errors in

void push_back(int val)
{
shared_ptr<A> temp = (new int(val),deleter);
cout << temp.use_count() << endl;
}

The line

shared_ptr<A> temp = (new int(val),deleter);

should be

shared_ptr<A> temp = shared_ptr<A>(new int(val),deleter);
//or
shared_ptr<A> temp(new int(val),deleter);

Then

new int(val)

is not correct since you are creating a int* and trying to initialize the pointer temp holds with that but you cant do that since temp holds a A*, not an int*. If you want to set the value of the val member then you need a constructor for A taking an int. Having

A(int val) : val(val) {}

would let you write

shared_ptr<A> temp(new A(val),deleter);

but there is still a problem with deleter. deleter is a non static member function which means it cannot be called without an instance of the class. That means you either need to make deleter static, which in this case is fine since you don't need the A object in deleter. If you need to have a non static member function then you can use a lambda to bind together an object with deleter. That would look like

shared_ptr<A> temp(new A(val),[this](auto ptr){ deleter(ptr) }); // capture the current object
//or
A delete_obj(some_value);
shared_ptr<A> temp(new A(val),[=](auto ptr){ delete_obj.deleter(ptr) }); // captures a object for itself

Shared_ptr custom deleter

It seems that you're trying to define a type alias that means "std::shared_ptr with my deleter type". There's no such thing, because std::shared_ptr has a type-erased deleter (the deleter is not part of the type).

Instead, you could create a custom version of make_shared:

template <class... Args>
std::shared_ptr<SDL_Surface> make_sdl_surface(Args&&... args) {
return std::shared_ptr<SDL_Surface>(new SDL_Surface(std::forward<Args>(args)...),
SDL_Surface_Deleter{});
}

std::shared_ptr with std::function as custom deleter and allocator

The allocator is not supposed to handle your data allocation, it should handle the allocation of shared_ptr's internal data.

You need provide the already allocated data as first argument to the constructor. shared_ptr takes only care the of memory clean up.

Furthermore the template argument of shared_ptr needs to be the type without indirection.

#include <functional>
#include <memory>
int main()
{
std::function<void(unsigned char*)> deleter
= [](unsigned char* ptr){
delete[] ptr;
};
std::shared_ptr<unsigned char> mem(new unsigned char[5], deleter);
return EXIT_SUCCESS;
}

If you can you should prefer a lambda over std::function, it leaves the compiler possibilities to optimize.

How to properly use the custom shared_ptr deleter?

In my cleanup method, do I have to delete T? Is it always safe to do it?

If the pointer references an object instantiated with new then you need to call delete otherwise you end up with a memory leak and undefined behavior.

What happens if cleanup() can throw an exception? Can I let it escape the scope under some circumstances, or should I always prevent it?

It shouldn't and you should make every effort to ensure that it doesn't. However if the cleanup code does throw an exception you should catch it, handle it appropriately, and eat it. The reason is the custom deleter can be invoked while in the context of a destructor and there is always the possibility the destructor is being invoked while an exception is already being propagated. If an exception is already in progress and another uncaught exception is thrown the application will terminate. In other words treat the custom deleter and cleanup code as if it were a destructor and follow the same rules and guidelines regarding exception handling.

Effective C++ Item #8 - Prevent exceptions from leaving destructors

Destructors should never emit exceptions. If functions called in a destructor may throw, the
destructor should catch any exceptions, then swallow them or terminate the program.

§ 15.1/7 C++ Standard [except.throw]

If the exception handling mechanism, after completing evaluation of the expression to be thrown but before the exception is caught, calls a function that exits via an exception, std::terminate is called.

-

Is this a sound design approach?

With the exception of how you currently intend on handling exceptions, I see nothing wrong with it. The only real changes you need to make is how you are invoking the callback and how the callback handles the exception passed to it. The resulting code after the changes might look something like below.

void Release(Resource* r)
{
try
{
// Do cleanup.
}
catch (Exception& ex)
{
// Report to callback
if (nullptr != m_callback)
m_callback(args, &ex);

// Handle exception completely or terminate

// Done
return;
}

// No exceptions, call with nullptr
if (nullptr != m_callback)
m_callback(args, nullptr);
}

void Callback(args, const Exception* ex)
{
// Emit telemetry, including exception information.

// DO NOT RETHROW ex
}

std::shared_ptr: typedef with custom deleter

Custom deleter is passed to shared_ptr in constructor, so it can't be done using typedef (deleter is not part of type of instantiated shared_ptr).

It could be done for unique_ptr (where deleter is part of type).

My suggestion: create factory method that will produce SDLWindowPtr (assigning them proper deleter). Something like std::make_shared but dedicated for your system (ex. createSDLWindow ?).

shared_ptr assignment - is custom deleter copied too?

Yes. The deleter is set when the object is created. Shared pointers manage the reference count in the control structure that has the deleter.

std::shared_ptr<A> a(new A, D());

This line creates a new shared object with a control structure that contains a deleter and a reference count of one. It also creates a shared pointer to that object and control structure.

std::shared_ptr<A> b;
b = a;

This creates a second reference to that object and control structure, bumping its reference count to two.

The same structure that holds the one and only reference count for the shared object also contains the deleter.



Related Topics



Leave a reply



Submit