2013-04-10 12 views
13

質問は厳密に約std::functionであり、boost::functionではありません。詳細については、この質問の最後にあるアップデートのセクションを参照してください。特に、C++ 11標準で空でないstd::functionオブジェクトを比較することはできません。std :: functionがコールバックとして登録解除可能ですか?


C++ 11 std::functionクラステンプレートは、コールバックのコレクションを維持するのに最適です。たとえば、それらをvectorに格納し、必要に応じて呼び出すことができます。しかし、これらのオブジェクトを維持し、登録解除を許可することは不可能であるようです。

は、このクラスを想像し、私は具体的にしてみましょう:

class Invoker 
{ 
public: 
    void Register(std::function<void()> f); 
    void Unregister(std::function<void()> f); 

    void InvokeAll(); 

private: 
    // Some container that holds the function objects passed to Register() 
}; 

サンプル使用シナリオ:

void foo() 
{ 
} 

int main() 
{ 
    std::function<void()> f1{foo}; 
    std::function<void()> f2{[] {std::cout << "Hello\n";} }; 

    Invoker inv; 

    // The easy part 

    // Register callbacks 
    inv.Register(f1); 
    inv.Register(f2); 

    // Invoke them 
    inv.InvokeAll(); 

    // The seemingly impossible part. How can Unregister() be implemented to actually 
    // locate the correct object to unregister (i.e., remove from its container)? 
    inv.Unregister(f2); 
    inv.Unregister(f1); 
} 

Register()機能を実現することができるか、かなり明白です。しかし、どうすればUnregister()を実装するのでしょうか。関数オブジェクトを保持するコンテナがvector<std::function<void()>>であるとします。 Unregister()呼び出しに渡される特定の関数オブジェクトをどのように見つけることができますか? std::functionは、オーバーロードされたoperator==を提供しますが、空の関数オブジェクト(つまり、2つの空ではない関数オブジェクトを比較して、両方が同じ実際の呼び出しを参照しているかどうかを確認することはできません)

私は何か考えていただければ幸いです。

更新:

アイデアこれまで主に、それを登録解除するために使用することができ、各std::functionオブジェクトに関連付けられるクッキーの添加から成ります。私はstd::functionオブジェクト自体に外生的ではない何かを望んでいました。また、std::functionboost::functionの間に多くの混乱があるようです。問題は厳密に約std::functionオブジェクトであり、ではなく、boost::functionオブジェクトです。

また、空でない2つのstd::functionオブジェクトを比較することはできません。彼らは常に、標準に準拠していないものと比較します。ですから、この問題の文脈では、コメントだけでそれを行うソリューション(そしてboost::functionオブジェクトを起動する)へのリンクが間違っています。

+5

接続ポイントでMSが行ったことを実行できます。発信者が登録抹消に提供するCookie(ベクトルへのインデックス)を返します。 – WhozCraig

+2

私はしばしばコールバックとしてインターフェイスポインタを格納し、それをセットに入れます。それから私は値でアクセスして登録を解除することができます。 –

+0

@RogerRowlandいいアイデア。 – WhozCraig

答えて

11

コンテナの要素IDをテストできないため、コンテナが変更されたときにイテレータが無効にならないコンテナ(たとえばstd::list)を使用し、イテレータを登録可能な呼び出し元に戻すことをお勧めします登録を解除するために使用されます。

実際にvector(またはdeque)を使用する場合は、コールバックを追加するときに、整数インデックスをvector/dequeに戻すことができます。この戦略では、シーケンス内の関数の位置を識別するために、索引がこのように使用可能であることを必ず確認する必要があります。コールバックや登録解除がまれである場合は、スポットを再利用しないことを意味します。または、空のスロットを再利用するための空きリストを実装することもできます。または、シーケンスの両端から空のスロットを再生し、スロットが最初から再生されたときに増加するベースインデックスオフセットを維持します。

コールバックアクセスパターンにランダムアクセストラバーサルが必要ない場合は、コールバックをstd::listに格納し、生のイテレータを使用して登録を解除するのが最も簡単です。