Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binding to a weak_ptr

Tags:

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
like image 303
Scotty Avatar asked Jul 27 '12 02:07

Scotty


People also ask

How can a weak_ptr be turned into a shared_ptr?

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.

Can a weak_ptr point to a Unique_ptr?

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.

How is weak_ptr implemented?

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.

When should I use weak_ptr?

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.


2 Answers

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()
});
like image 73
Nicol Bolas Avatar answered Oct 05 '22 19:10

Nicol Bolas


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.
like image 40
Tim Finer Avatar answered Oct 05 '22 19:10

Tim Finer