简体   繁体   English

绑定到weak_ptr

[英]Binding to a weak_ptr

Is there a way to std::bind to a std::weak_ptr ? 有没有办法将std :: bind绑定到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: 我知道如何使用shared_ptr创建一个std :: function:

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. 但是返回的std :: function使我的对象永远保持活着。 So I'd like to bind it to a weak_ptr : 所以我想将它绑定到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? (std :: bind将不接受weak_ptr!)有没有办法绑定到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? 存储“弱功能”的最佳解决方案是什么,特别是如果Boost不可用?


Discussions / research (all of these use Boost and are not standardized): 讨论/研究(所有这些都使用Boost并且没有标准化):

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 . MyClass::CallbackFuncMyClass类的非静态成员函数。 Being a non-static member function, it must be called with a valid instance of MyClass . 作为非静态成员函数, 必须使用MyClass有效实例调用它。

The entire point of weak_ptr is that it isn't necessarily valid. 整个点 weak_ptr是,它并不一定有效的。 You can detect its validity by transforming it into a shared_ptr and then testing if the pointer is NULL. 您可以通过将其转换为shared_ptr来检测其有效性,然后测试指针是否为NULL。 Since weak_ptr is not guaranteed to be valid at all times, you cannot call a non-static member function with one. 由于不能保证weak_ptr始终有效, 因此无法使用一个非静态成员函数调用。

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. 最好的办法是使用实​​际逻辑,如果weak_ptr无效,则不调用回调函数。 bind is not designed to do logic; bind不是为了做逻辑而设计的; it just does exactly what you tell it to: call the function. 它只是完全按照你的说法去做:调用函数。 So you need to use a proper lambda: 所以你需要使用一个合适的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). 我能够创建st_ :: function的weak_pointers并使用clang-3.2测试它(你没有给出任何编译器限制)。

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.
#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

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. 对我来说最终的解决方案是返回一个函数对象,它返回一个boost :: optional <>,具体取决于函数是否被调用。

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 源代码和测试可在此处获得: https//github.com/madmongo1/valuelib

Not sure why that definition is not in boost. 不确定为什么这个定义没有提升。 There must be a good reason (how to deal with lock fail? Is throwing from there acceptable? Thread safety?) Anyway, that will validate your callee. 必须有一个很好的理由(如何处理锁定失败?从那里投掷可接受?线程安全?)无论如何,这将验证你的被调用者。

namespace boost {

template<class T> T * get_pointer(boost::weak_ptr<T> const& p)
{
  boost::shared_ptr< T > _strong = p.lock();
  if( _strong )
   return _strong.get();
  else
    throw 1;
}

}

int main(int arg, char *argv[])
{
  boost::weak_ptr< MyType > weak_bad;
  {
    boost::shared_ptr< MyType > strong(new MyType);
    boost::weak_ptr< MyType > weak(strong);
    boost::function< void(int) > _func1 = boost::bind(&MyType::setX, weak, _1);
    _func1(10);
    weak_bad = strong;
  }

  try {
    boost::function< void(int) > _func1 = boost::bind(&MyType::setX, weak_bad, _1);
    _func1(10);
  }
  catch(...)
  {
    std::cout << "oops!";
  }

  return 0;
};

Another solution: 另一种方案:

You could wrap the std::function. 你可以包装std :: function。 The class producing the callback would hold a shared_ptr< wrapper_type > and provide a weak_ptr< wrapper_type >. 产生回调的类将包含shared_ptr <wrapper_type>并提供weak_ptr <wrapper_type>。 The producing object would be the one with the ownership, if it goes out of scope, callers won't be able to promote their weak reference. 生产对象将是拥有所有权的对象,如果超出范围,则呼叫者将无法宣传其弱引用。 Your wrapper type could forward call arguments to the std::function or simply expose it via its interface. 您的包装器类型可以将调用参数转发给std :: function,或者只是通过其接口公开它。 Just make sure that on copy you properly handle the shared_ptr on the wrapper (don't share). 只需确保在复制时正确处理包装器上的shared_ptr(不要共享)。

template< typename _Ty >
struct wrapper
{
  wrapper(_Ty wrappe) 
    : _wrappe(wrappe)
  { }

  _Ty _wrappe;
};

...
boost::shared_ptr< wrapper < std::func< ... > > _func(new wrapper < std::func< ... > );

...
boost::weak_ptr< wrapper < std::func< ... > getCallBack() {
  return _func;
}

How about this? 这个怎么样? it works only for actions std::function<void()> but perhaps it can be generalized for arbitrarily parameterized functions. 它仅适用于动作std::function<void()>但也许它可以针对任意参数化函数进行推广。

#include <memory>
#include <functional>

template<typename T>
void 
perform_action_or_ignore_when_null(
    std::weak_ptr<T> weak, 
    std::function< void( std::shared_ptr<T> ) > func
    )
{
    if(auto ptr = weak.lock())
        func(ptr);
}

template<typename T>
std::function<void()> 
ignore_when_null(
    std::weak_ptr<T> weak, 
    std::function< void( std::shared_ptr<T> ) > func
    )
{
    return std::bind(perform_action_or_ignore_when_null<T>, weak, func);
}

here's an example usage: 这是一个示例用法:

struct Foo {
    Foo() {}
    void bar() { 
        std::cout << "hello world!" << std::endl;
    }
};

void main()
{
  std::weak_ptr<Foo> weakfoo;
  std::function<void(std::shared_ptr<Foo>)> foobar = std::bind(&Foo::bar, std::placeholders::_1);
  {
     auto foo = std::make_shared<Foo>();
     weakfoo  = foo;

     auto f = ignore_when_null(weakfoo, foobar);
     f(); // prints "hello world!";
   }

   auto g = ignore_when_null(weakfoo, foobar);
   g(); // does nothing
}

You can bind weak_ptr to the function as one of parameters, 你可以将weak_ptr绑定到函数作为参数之一,
and check it when the function is called. 并在调用函数时检查它。

For example: 例如:

std::function<void()> MyClass::GetCallback()
{
    std::weak_ptr<MyClass> thisWeakPtr(shared_from_this());
    return std::function<void()>(std::bind(&MyClass::CallbackFunc, this,
                                           thisWeakPtr));
}

void MyClass::CallbackFunc(const std::weak_ptr<MyClass>& thisWeakPtr)
{
  if (!thisWeakPtr.lock()) {
    return;
  }

  // Do your callback job.
  // ...
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM