[英]Binding to a weak_ptr
有沒有辦法將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並且沒有標准化):
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()
});
我能夠創建st_ :: 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.
#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
我知道這是一個老問題,但我有同樣的要求,我相信我並不孤單。
對我來說最終的解決方案是返回一個函數對象,它返回一個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
不確定為什么這個定義沒有提升。 必須有一個很好的理由(如何處理鎖定失敗?從那里投擲可接受?線程安全?)無論如何,這將驗證你的被調用者。
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 <wrapper_type>並提供weak_ptr <wrapper_type>。 生產對象將是擁有所有權的對象,如果超出范圍,則呼叫者將無法宣傳其弱引用。 您的包裝器類型可以將調用參數轉發給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;
}
這個怎么樣? 它僅適用於動作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);
}
這是一個示例用法:
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
}
你可以將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.
// ...
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.