【问题标题】:Binding to a weak_ptr绑定到一个weak_ptr
【发布时间】:2012-07-25 17:04:36
【问题描述】:

有没有办法std::bind 到 std::weak_ptr?我想存储一个“弱函数”回调,当被调用者被销毁时会自动“断开”。

我知道如何使用 shared_ptr 创建 std::function:

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

然而,返回的 std::function 让我的对象永远活着。所以我想将它绑定到 weak_ptr

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

但这不会编译。 (std::bind 将不接受weak_ptr!)有没有办法绑定到weak_ptr?

我找到了有关此的讨论(见下文),但似乎没有标准实现。存储“弱功能”的最佳解决方案是什么,尤其是在 Boost 不可用的情况下?


讨论/研究(所有这些都使用 Boost 并且没有标准化):

【问题讨论】:

  • 也许一个信号库(如 Boost.Signals2)会满足您的需求。
  • @LucDanton:嗯,是的,你是对的。如果是这样的话,weak_fn 是否仍然存在任何用例?
  • 您是否尝试过使用带有 shared_ptr 的 weak_ptr 构造函数?
  • @TimFiner:我知道如何从 shared_ptr 创建一个weak_ptr。 :) 问题是 std::function 构造函数不能采用weak_ptr。

标签: c++ callback bind weak-ptr std-function


【解决方案1】:
std::weak_ptr<MyClass> thisWeakPtr(shared_from_this());
return std::function<void()>(std::bind(&MyClass::CallbackFunc, thisWeakPtr));

你不应该这样做。 永远。

MyClass::CallbackFunc 是类MyClass 的非静态成员函数。作为一个非静态成员函数,它必须使用MyClass有效实例调用。

weak_ptr全部要点是它不一定有效。您可以通过将其转换为shared_ptr 来检测其有效性,然后测试指针是否为 NULL。由于weak_ptr 不能保证在任何时候都有效,因此您不能使用一个来调用非静态成员函数。

你所做的并不比:

std::bind(&MyClass::CallbackFunc, nullptr)

它可以编译,但当你尝试调用它时它最终会崩溃。

您最好的选择是使用实际逻辑,如果weak_ptr 无效,则不调用回调函数。 bind 不是用来做逻辑的;它只是按照你告诉它的去做:调用函数。所以你需要使用合适的 lambda:

std::weak_ptr<MyClass> thisWeakPtr(shared_from_this());
return std::function<void()>([thisWeakPtr]()
{
  auto myPtr = thisWeakPtr.lock();
  if(myPtr)
    myPtr->CallbackFunc()
});

【讨论】:

  • 是的,lambda 是我得出的结论。作为记录,正如我在 OP 中所述,带有 weak_ptr 的 bind() 不会编译(至少在 MSVC 上)。是否可以使这个 lambda 通用(模板化),这样我就不需要为每个weak_ptr 编写自定义 lambda?
  • @Scotty:它并不需要专门是一个 lambda。它只需要一个函子。如果您愿意,这样的函数对象可以是模板。
  • 当然。您想提供一个模板示例以便其他人受益吗? :) 我想接受这个答案,但我觉得“你永远不应该这样做”部分具有误导性,因为绑定到一个 weak_ptr 无法完成 - 这不是“应该”。只需 lambda(最好是模板化的)就足以回答这个问题。
  • 我刚刚意识到以前的版本抛出了一个异常bad_weak_ptr,所以带有auto的当前版本也修复了一个错误。见shared_ptr constructor version 11
【解决方案2】:

我能够创建 std::function 的 weak_pointers 并使用 clang-3.2 对其进行测试(您没有给出任何编译器限制)。

这是一个示例应用程序,它创建并测试了我认为您所要求的内容:

#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;
}

输出是:

~/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.

【讨论】:

  • 我正在尝试将一个weak_ptr 传递给std::function 的构造函数,而不是将std::function 存储在shared_ptr/weak_ptr 中。我正在编辑问题以使其更清楚。不过谢谢你的回答。 :)
  • 不客气。对不起,我还没有得到你的要求。 std::function 没有构造函数,所以我真的很困惑你所说的 std::function 的构造函数是什么意思。您可以定义 std::function 接受哪​​些参数以及它返回什么,并且对那些是 AFAIK 没有任何限制。
  • 抱歉,不清楚 - 我添加了示例来说明我的意思。我需要使用weak_ptr进行绑定,然后将其传递给std::function的构造函数。
【解决方案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;
}

输出:

abc
bad_weak_ptr

【讨论】:

    【解决方案4】:

    我知道这是一个老问题,但我也有同样的要求,我相信我并不孤单。

    对我来说最终的解决方案是返回一个函数对象,该对象返回一个 boost::optional 取决于函数是否被调用。

    代码在这里:

    #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)...)
            };
        }
    }}
    

    这样称呼(例如):

    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));
    
    
    }
    

    此处提供源代码和测试: https://github.com/madmongo1/valuelib

    【讨论】:

      【解决方案5】:

      不确定为什么该定义不在 boost 中。必须有充分的理由(如何处理锁失败?从那里抛出可以接受吗?线程安全?)无论如何,这将验证你的被调用者。

      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;
      };
      

      另一种解决方案:

      你可以包装 std::function。产生回调的类将持有一个 shared_ptr 并提供一个 weak_ptr。生产对象将是拥有所有权的对象,如果它超出范围,调用者将无法提升他们的弱引用。您的包装器类型可以将调用参数转发给 std::function 或简单地通过其接口公开它。只需确保在复制时正确处理包装器上的 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;
      }
      

      【讨论】:

      • 再考虑一下...可能不是线程安全的...因为在 get_pointer 调用之后没有任何东西持有引用。
      【解决方案6】:

      这个怎么样?它仅适用于动作std::function&lt;void()&gt;,但也许它可以推广到任意参数化的函数。

      #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);
      }
      

      这是一个示例用法:

      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
      }
      

      【讨论】:

        【解决方案7】:

        您可以将weak_ptr 作为参数之一绑定到函数,
        并在调用函数时检查它。

        例如:

        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.
          // ...
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-10-18
          • 2015-12-16
          • 1970-01-01
          • 1970-01-01
          • 2020-08-26
          • 2012-10-13
          • 2016-06-03
          • 2012-09-17
          相关资源
          最近更新 更多