Is there a way to std::bind to a std::weak_ptr? I'd like to store a "weak function" callback that automatically "disconnects" when the callee is destroyed.
I know how to create a std::function using a shared_ptr:
std::function<void()> MyClass::GetCallback()
{
return std::function<void()>(std::bind(&MyClass::CallbackFunc, shared_from_this()));
}
However the returned std::function keeps my object alive forever. So I'd like to bind it to a weak_ptr:
std::function<void()> MyClass::GetCallback()
{
std::weak_ptr<MyClass> thisWeakPtr(shared_from_this());
return std::function<void()>(std::bind(&MyClass::CallbackFunc, thisWeakPtr));
}
But that doesn't compile. (std::bind will accept no weak_ptr!) Is there any way to bind to a weak_ptr?
I've found discussions about this (see below), but there seems to be no standard implementation. What is the best solution for storing a "weak function", in particular if Boost is not available?
Discussions / research (all of these use Boost and are not standardized):
The weak_ptr class template stores a "weak reference" to an object that's already managed by a shared_ptr. To access the object, a weak_ptr can be converted to a shared_ptr using the shared_ptr constructor or the member function lock.
You can implement weak_ptr which works correctly with unique_ptr but only on the same thread - lock method will be unnecessary in this case.
To implement weak_ptr , the "counter" object stores two different counters: The "use count" is the number of shared_ptr instances pointing to the object. The "weak count" is the number of weak_ptr instances pointing to the object, plus one if the "use count" is still > 0.
By using a weak_ptr , you can create a shared_ptr that joins to an existing set of related instances, but only if the underlying memory resource is still valid. A weak_ptr itself does not participate in the reference counting, and therefore, it cannot prevent the reference count from going to zero.
std::weak_ptr<MyClass> thisWeakPtr(shared_from_this());
return std::function<void()>(std::bind(&MyClass::CallbackFunc, thisWeakPtr));
You should never do this. Ever.
MyClass::CallbackFunc
is a non-static member function of the class MyClass
. Being a non-static member function, it must be called with a valid instance of MyClass
.
The entire point of weak_ptr
is that it isn't necessarily valid. You can detect its validity by transforming it into a shared_ptr
and then testing if the pointer is NULL. Since weak_ptr
is not guaranteed to be valid at all times, you cannot call a non-static member function with one.
What you're doing is no more valid than:
std::bind(&MyClass::CallbackFunc, nullptr)
It may compile, but it will eventually crash when you try to call it.
Your best bet is to use actual logic, to not call the callback function if the weak_ptr
is not valid. bind
is not designed to do logic; it just does exactly what you tell it to: call the function. So you need to use a proper lambda:
std::weak_ptr<MyClass> thisWeakPtr(shared_from_this());
return std::function<void()>([thisWeakPtr]()
{
auto myPtr = thisWeakPtr.lock();
if(myPtr)
myPtr->CallbackFunc()
});
I was able to create weak_pointers of std::function and tested it with clang-3.2 (you didn't give any compiler restrictions).
Here's a sample app that creates and tests what I believe you are asking for:
#include <functional>
#include <memory>
#include <iostream>
typedef std::function<void(void)> Func;
typedef std::shared_ptr<Func> SharedFunc;
typedef std::weak_ptr<Func> WeakFunc;
void Execute( Func f ) {
f();
}
void Execute( SharedFunc sf ) {
(*sf)();
}
void Execute( WeakFunc wf ) {
if ( auto f = wf.lock() )
(*f)();
else
std::cout << "Your backing pointer went away, sorry.\n";
}
int main(int, char**) {
auto f1 = [](){ std::cout << "Func here.\n"; };
Execute( f1 );
auto f2 = [](){ std::cout << "SharedFunc here.\n"; };
SharedFunc sf2( new Func(f2) );
Execute( sf2 );
auto f3 = [](){ std::cout << "WeakFunc here.\n"; };
SharedFunc sf3( new Func(f3) );
WeakFunc wf3( sf3 );
Execute( wf3 );
// Scoped test to make sure that the weak_ptr is really working.
WeakFunc wf4;
{
auto f4 = [](){ std::cout << "You should never see this.\n"; };
SharedFunc sf4( new Func(f4) );
wf4 = sf4;
}
Execute( wf4 );
return 0;
}
The output was:
~/projects/stack_overflow> clang++-mp-3.2 --std=c++11 --stdlib=libc++ weak_fun.cpp -o wf && ./wf
Func here.
SharedFunc here.
WeakFunc here.
Your backing pointer went away, sorry.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With