4

thisオブジェクトのメンバー関数にバインドされた関数を削除する方法:

std::vector<std::function<void(int)>> callbacks;

class MyClass {
public:
    MyClass() {
        callbacks.push_back(
            std::bind(&MyClass::myFunc,this,std::placeholders::_1)
        );
    }
    ~MyClass() {
        auto it = std::remove_if( std::begin(callbacks),
                                  std::end(callbacks),
                                  [&](std::function<void(int)>& f) {
                return // <-- this is my question
                       //     true (remove) if f is bound to member function 
                       //     of this
        });
        callbacks.erase(it,std::end(callbacks));
    }
    void myFunc(int param){...}
};
4

3 に答える 3

4
    typedef decltype(std::bind(&MyClass::myFunc,this,std::placeholders::_1)) bound_type;

    auto it = std::remove_if( std::begin(callbacks),
                              std::end(callbacks),
                              [](const std::function<void(int)>& f) {
          return f.target<bound_type>() != nullptr;
    });

メンバー関数テンプレートstd::function::target<T>は、タイプが の場合はターゲット オブジェクトへのポインターを返しT、それ以外の場合は null を返します。したがって、 から取得できるターゲット オブジェクトのタイプに名前を付けることができる必要がありますdecltype。とてもシンプルです:-)

破棄される特定のオブジェクトのポインターをバインドしたものだけでなく、そのタイプのコールバックをすべて削除する注意。thisオブジェクトが破棄された後にコールバックが呼び出されないようにしようとしていて、ベクトルのどの要素がどのオブジェクトを参照しているかを特定する方法がない場合は、クラスに shared_ptr を配置してから、weak_ptr をクラスに格納することを検討できます。オブジェクトが破棄されたかどうかを検出するために使用できるコールバック:

class MyClass
{
    struct NullDeleter { void operator()(void*) const { } };
    std::shared_ptr<MyClass> sp;

    static void safe_invoke(void (MyClass::*f)(int), const std::weak_ptr<MyClass>& wp, int i)
    {
        if (std::shared_ptr<MyClass> safe_this = wp.lock())
            (safe_this.get()->*f)(i);
    }

public:
    MyClass() : sp(this, NullDeleter()) {
        callbacks.push_back(
            std::bind(safe_invoke, &MyClass::myFunc ,std::weak_ptr<MyClass>(sp),
                      std::placeholders::_1)
        );
    };

これにより、メンバー関数を呼び出す前に、をにinvoke変換する関数でメンバー関数の呼び出しがラップされます。オブジェクトが破棄された場合、 は空になるため、関数は何もしません。これは、無効になったときにコールバックを実際に削除するわけではありませんが、安全に呼び出すことができます。weak_ptrshared_ptrshared_ptr

于 2012-06-27T16:40:34.063 に答える
2

一般的なケースでは、余分な作業が必要です。型消去は、オブジェクトからこの情報をクリアし、std::functionこの情報を直接公開しません。

あなたの特定の例には、削除する候補となる可能性のあるメンバー関数が1つしかない場合がありますが、コールバックとして格納できる5つのメンバーを持つクラスはどうでしょうか。それらすべてをテストする必要があります。また、ラムダを使用してメンバー関数をバインドすることもできますが、これはほとんど検出できません。

次の場合の1つの解決策は次のとおりです。

  • すべてのコールバックは内部から登録されますMyClass
  • コンテナは、追加情報を格納するように修正されています
  • あなたはすべての余分な簿記を喜んでします
std::vector<std::pair<std::function<void(int)>, void*>> callbacks;

class MyClass{
  static unsigned const num_possible_callbacks = 2; // keep updated
  std::array<std::type_info const*, num_possible_callbacks> _infos;
  unsigned _next_info;

  // adds type_info and passes through
  template<class T>
  T const& add_info(T const& bound){
    if(_next_info == num_possible_callbacks)
      throw "oh shi...!"; // something went out of sync
    _infos[_next_info++] = &typeid(T);
    return bound;
  }
public:
  MyClass() : _next_info(0){
    using std::placeholders::_1;
    callbacks.push_back(std::make_pair(
        add_info(std::bind(&MyClass::myFunc, this, _1)),
        (void*)this));
    callbacks.push_back(std::make_pair(
        add_info([this](int i){ return myOtherFunc(i, 0.5); }),
        (void*)this));
  }

  ~MyClass(){
    using std::placeholders::_1;

    callbacks.erase(std::remove_if(callbacks.begin(), callbacks.end(),
        [&](std::pair<std::function<void(int)>, void*> const& p) -> bool{
          if(p.second != (void*)this)
            return false;
          auto const& f = p.first;
          for(unsigned i = 0; i < _infos.size(); ++i)
            if(_infos[i] == &f.target_type())
              return true;
          return false;
        }), callbacks.end());
  }

  void myFunc(int param){ /* ... */ }
  void myOtherFunc(int param1, double param2){ /* ... */ }
};

Ideoneの実例。

于 2012-06-27T15:05:58.390 に答える
2

私はかつてこのようなことをする必要があり、関数を含むクラスにオブジェクトの共有ポインターのベクトルを格納し、それらが破棄されたときに値によってベクトルから関数を削除することで解決しました。これにより、これも自動になります。

于 2012-06-27T17:48:41.517 に答える