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
Enum VS Constexpr for Actual Static Constants Inside Classes
How to Merge Two Bst's Efficiently
Eclipse C++:"Program "G++" Not Found in Path"
Vary Range of Uniform_Int_Distribution
Lnk2019: Unresolved External Symbol _Main Referenced in Function _Tmaincrtstartup
Should Objects Delete Themselves in C++
Making a Borderless Window with for Qt
When to Use Vectors and When to Use Arrays in C++
Creating Library with Backward Compatible Abi That Uses Boost
C++ Memory Barriers for Atomics
How to Return Array from C++ Function to Python Using Ctypes
Why Don't Std::Vector's Elements Need a Default Constructor