Binding to a weak_ptr

2019-01-23 13:45发布

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):

7条回答
唯我独甜
2楼-- · 2019-01-23 14:39

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

查看更多
登录 后发表回答