20

std::weak_ptr に std::bind する方法はありますか? 呼び出し先が破棄されたときに自動的に「切断」する「弱い関数」コールバックを保存したいと思います。

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 を使用しており、標準化されていません):

4

7 に答える 7

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

これは絶対にしないでください。これまで。

MyClass::CallbackFuncクラス の非静的メンバー関数ですMyClass。非静的メンバー関数であるため、 の有効なインスタンスで呼び出す必要があります。MyClass

要点は、必ずしも有効ではないweak_ptrということです。に変換し、ポインターが NULL かどうかをテストすることで、その有効性を検出できます。常に有効であるとは限らないため、非静的メンバー関数を呼び出すことはできません。shared_ptrweak_ptr

あなたがしていることは、以下よりも有効ではありません:

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

コンパイルすることはできますが、呼び出そうとすると最終的にクラッシュします。

あなたの最善の策は、実際のロジックを使用して、weak_ptr有効でない場合はコールバック関数を呼び出さないことです。bindロジックを実行するように設計されていません。まさにあなたが指示したことを実行するだけです: 関数を呼び出します。したがって、適切なラムダを使用する必要があります。

std::weak_ptr<MyClass> thisWeakPtr(shared_from_this());
return std::function<void()>([thisWeakPtr]()
{
  auto myPtr = thisWeakPtr.lock();
  if(myPtr)
    myPtr->CallbackFunc()
});
于 2012-09-01T21:38:57.547 に答える
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.
于 2012-07-30T15:36:37.187 に答える
1

これは古い質問であることはわかっていますが、同じ要件があり、私だけではないと確信しています。

私にとっての最終的な解決策は、関数が呼び出されたかどうかに応じて 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

于 2016-02-09T16:52:50.987 に答える
1
#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
于 2015-04-29T19:00:07.337 に答える
0

その定義が後押しされていない理由がわかりません。正当な理由があるはずです(ロックの失敗に対処する方法?そこからスローすることは許容できますか?スレッドセーフですか?)とにかく、それはあなたの呼び出し先を検証します。

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;
}
于 2012-09-01T20:36:35.277 に答える
0

weak_ptrパラメータの 1 つとして関数にバインドし、
関数が呼び出されたときにそれをチェックできます。

例えば:

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.
  // ...
}
于 2014-11-17T06:15:30.090 に答える