Binding to a weak_ptr
Solution 1
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()
});
Solution 2
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.
Solution 3
#include <iostream>
#include <string>
#include <memory>
#include <functional>
using namespace std;
template < typename T > class LockingPtr {
std :: weak_ptr < T > w;
public:
typedef shared_ptr < T > result_type;
LockingPtr ( const std :: shared_ptr < T > & p ) : w ( p ) { }
std :: shared_ptr < T > lock ( ) const {
return std :: shared_ptr < T > ( w );
}
std :: shared_ptr < T > operator-> ( ) const {
return lock ( );
}
template < typename ... Args > std :: shared_ptr < T > operator( ) ( Args ... ) const {
return lock ( );
}
};
template < typename T > LockingPtr < T > make_locking ( const shared_ptr < T > & p ) {
return p;
}
namespace std {
template < typename T > struct is_bind_expression < LockingPtr < T > > :
public true_type { };
}
int main() {
auto p = make_shared < string > ( "abc" );
auto f = bind ( & string :: c_str, make_locking ( p ) );
cout << f ( ) << '\n';
p.reset ( );
try {
cout << f ( ) << '\n';
} catch ( const exception & e ) {
cout << e.what ( ) << '\n';
}
// your code goes here
return 0;
}
output:
abc
bad_weak_ptr
Solution 4
I know this is an old question, but I have the same requirement and I'm sure I'm not alone.
The solution in the end for me was to return a function object that returns a boost::optional<> depending on whether the function was called or not.
code here:
#include <boost/optional.hpp>
#include <memory>
namespace value { namespace stdext {
using boost::optional;
using boost::none;
struct called_flag {};
namespace detail
{
template<class Target, class F>
struct weak_binder
{
using target_type = Target;
using weak_ptr_type = std::weak_ptr<Target>;
weak_binder(weak_ptr_type weak_ptr, F f)
: _weak_ptr(std::move(weak_ptr))
, _f(std::move(f))
{}
template<class...Args,
class Result = std::result_of_t<F(Args...)>,
std::enable_if_t<not std::is_void<Result>::value>* = nullptr>
auto operator()(Args&&...args) const -> optional<Result>
{
auto locked_ptr = _weak_ptr.lock();
if (locked_ptr)
{
return _f(std::forward<Args>(args)...);
}
else
{
return none;
}
}
template<class...Args,
class Result = std::result_of_t<F(Args...)>,
std::enable_if_t<std::is_void<Result>::value>* = nullptr>
auto operator()(Args&&...args) const -> optional<called_flag>
{
auto locked_ptr = _weak_ptr.lock();
if (locked_ptr)
{
_f(std::forward<Args>(args)...);
return called_flag {};
}
else
{
return none;
}
}
weak_ptr_type _weak_ptr;
F _f;
};
}
template<class Ret, class Target, class...FuncArgs, class Pointee, class...Args>
auto bind_weak(Ret (Target::*mfp)(FuncArgs...), const std::shared_ptr<Pointee>& ptr, Args&&...args)
{
using binder_type = decltype(std::bind(mfp, ptr.get(), std::forward<Args>(args)...));
return detail::weak_binder<Target, binder_type>
{
std::weak_ptr<Target>(ptr),
std::bind(mfp, ptr.get(), std::forward<Args>(args)...)
};
}
}}
called (for example) like so:
TEST(bindWeakTest, testBasics)
{
struct Y
{
void bar() {};
};
struct X : std::enable_shared_from_this<X>
{
int increment(int by) {
count += by;
return count;
}
void foo() {
}
Y y;
int count = 0;
};
auto px = std::make_shared<X>();
auto wf = value::stdext::bind_weak(&X::increment, px, std::placeholders::_1);
auto weak_call_bar = value::stdext::bind_weak(&Y::bar, std::shared_ptr<Y>(px, &px->y));
auto ret1 = wf(4);
EXPECT_TRUE(bool(ret1));
EXPECT_EQ(4, ret1.get());
auto wfoo1 = value::stdext::bind_weak(&X::foo, px);
auto retfoo1 = wfoo1();
EXPECT_TRUE(bool(retfoo1));
auto retbar1 = weak_call_bar();
EXPECT_TRUE(bool(retbar1));
px.reset();
auto ret2 = wf(4);
EXPECT_FALSE(bool(ret2));
auto retfoo2 = wfoo1();
EXPECT_FALSE(bool(retfoo2));
auto retbar2 = weak_call_bar();
EXPECT_FALSE(bool(retbar2));
}
source code and tests available here: https://github.com/madmongo1/valuelib
Scotty
Updated on June 06, 2022Comments
-
Scotty about 2 years
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):
- weak_function
- weak_ptr binding
- "weak" binding (and a fix for it)
- weak_fn
- Another weak_fn