5

イベントを処理する関数を格納するために使用される tr1 関数テンプレートを使用して、C++ で C# のようなイベント システムを実装しようとしています。

複数のリスナーをこのイベントにアタッチできるように、ベクトルを作成しました。

vector< function<void (int)> >  listenerList;

リストからハンドラーを削除して、リスナーがイベントを受信するのを停止できるようにしたいと考えています。

では、特定のリスナーに対応するこのリスト内のエントリを見つけるにはどうすればよいでしょうか? リスト内の「関数」オブジェクトが特定の関数を参照しているかどうかをテストできますか?

ありがとう!

編集:boost::signal アプローチを調べたところ、一部の人が提案したように、おそらくトークン システムを使用して実装されているようです。これに関する情報は次のとおりです。オブザーバーは、イベントにアタッチするときに「接続」オブジェクトを保持します。この接続オブジェクトは、必要に応じて切断するために使用されます。というわけでBoostを使ってもtr1で自転しても基本原理は同じみたいです。つまり、少し不器用になります:)

4

7 に答える 7

8

std C++ と tr1 にロックされているかどうかはわかりませんが、そうでない場合は、boost::signal や boost::bind などを使用して問題を解決すれば、問題を完全に回避できるようです。独自の問題 - イベントシステムの作成 - 独自のロールを試行する代わりに。

于 2008-09-18T06:25:59.030 に答える
4

さて、あなたは私を働かせました。難しいのは、C# イベントの使用パターンを正確に一致させようとすることです。それをスキップすると、あなたが求めていることを行うためのはるかに簡単な方法があります. (私の同僚のジェイソンは、あらゆる場所で Notifier オブジェクトを使用しています。) とにかく、これはあなたが望むことを実行する信じられないほど退屈なコードです。残念ながら、サブジェクトからオブザーバーにパラメーターを渡すことはできません。そのためには、さらにスマートを追加する必要があります。

#include "stdafx.h"
#include <iostream>
#include <string>
#include <list>
#include <algorithm>
#include <boost/tr1/functional.hpp>
#include <boost/tr1/memory.hpp>

using namespace std;
using namespace std::tr1;

template <typename T>
class ObserverHandle
{
public:
    typedef boost::function<void (T*)> const UnderlyingFunction;

    ObserverHandle(UnderlyingFunction underlying)
        : _underlying(new UnderlyingFunction(underlying))
    {
    }

    void operator()(T* data) const
    {
        (*_underlying)(data);
    }

    bool operator==(ObserverHandle<T> const& other) const
    {
        return (other._underlying == _underlying);
    }

private:
    shared_ptr<UnderlyingFunction> const _underlying;
};

class BaseDelegate
{
public:
    virtual bool operator==(BaseDelegate const& other)
    {
        return false;
    }

    virtual void operator() () const = 0;
};

template <typename T>
class Delegate : public BaseDelegate
{
public:
    Delegate(T* observer, ObserverHandle<T> handle)
        : _observer(observer),
        _handle(handle)
    {
    }

    virtual bool operator==(BaseDelegate const& other)
    {
        BaseDelegate const * otherPtr = &other;
        Delegate<T> const * otherDT = dynamic_cast<Delegate<T> const *>(otherPtr);
        return ((otherDT) &&
            (otherDT->_observer == _observer) &&
            (otherDT->_handle == _handle));
    }

    virtual void operator() () const
    {
        _handle(_observer);
    }

private:
    T* _observer;
    ObserverHandle<T> _handle;
};

class Event
{
public:
    template <typename T>
    void add(T* observer, ObserverHandle<T> handle)
    {
        _observers.push_back(shared_ptr<BaseDelegate>(new Delegate<T>(observer, handle)));
    }

    template <typename T>
    void remove(T* observer, ObserverHandle<T> handle)
    {
        // I should be able to come up with a bind2nd(equals(dereference(_1))) kind of thing, but I can't figure it out now
        Observers::iterator it = find_if(_observers.begin(), _observers.end(), Compare(Delegate<T>(observer, handle)));
        if (it != _observers.end())
        {
            _observers.erase(it);
        }
    }

    void operator()() const
    {
        for (Observers::const_iterator it = _observers.begin();
            it != _observers.end();
            ++it)
        {
            (*(*it))();
        }
    }

private:
    typedef list<shared_ptr<BaseDelegate>> Observers;
    Observers _observers;

    class Compare
    {
    public:
        Compare(BaseDelegate const& other)
            : _other(other)
        {
        }

        bool operator() (shared_ptr<BaseDelegate> const& other) const
        {
            return (*other) == _other;
        }

    private:
        BaseDelegate const& _other;
    };
};

// Example usage:

class SubjectA
{
public:
    Event event;

    void do_event()
    {
        cout << "doing event" << endl;
        event();
        cout << "done" << endl;
    }
};

class ObserverA
{
public:
    void test(SubjectA& subject)
    {
        subject.do_event();
        cout << endl;

        subject.event.add(this, _observe);
        subject.do_event();
        subject.event.remove(this, _observe);
        cout << endl;

        subject.do_event();
        cout << endl;

        subject.event.add(this, _observe);
        subject.event.add(this, _observe);
        subject.do_event();
        subject.event.remove(this, _observe);
        subject.do_event();
        subject.event.remove(this, _observe);
        cout << endl;

    }

    void observe()
    {
        cout << "..observed!" << endl;
    }

private:
    static ObserverHandle<ObserverA> _observe;
};

// Here's the trick: make a static object for each method you might want to turn into a Delegate
ObserverHandle<ObserverA> ObserverA::_observe(boost::bind(&ObserverA::observe, _1));

int _tmain(int argc, _TCHAR* argv[])
{
    SubjectA sa;
    ObserverA oa;
    oa.test(sa);

    return 0;
}

出力は次のとおりです。

イベントを
行う

やってるイベント
に注目!
終わり

イベントを
行う

やってるイベント
に注目!
..観察!
やっ
てるイベント
に注目!
終わり

于 2008-09-18T04:46:25.480 に答える
2

ブースト機能のドキュメントのFAQ #1はあなたの質問に答えているようです - そして簡単な答えは「いいえ」です。

于 2008-09-18T02:42:03.033 に答える
1

提案(セクション IIIb.) は、それらはいかなる方法でも比較できないと述べています。それらに追加情報を追加すると、各コールバックを簡単に識別できます。たとえば、関数ポインターをラップする構造体を定義するだけであれば、それらを削除できます (挿入した同じ構造体があると仮定します)。構造体にいくつかのフィールドを追加して (クライアントが保持できる自動生成された GUID など)、それと比較することもできます。

于 2008-09-18T02:47:04.003 に答える
0

関数ポインターのみを格納する (必要な署名に一致する他のファンクターを格納しない) 場合、これは簡単です (以下のコードを参照)。しかし、一般的に、他の投稿者が言っているように、答えはノーです。その場合、ファンクターを値としてハッシュに格納し、ユーザーが追加および削除時にキーを指定することをお勧めします。

以下のコードは、呼び出されるファンクター/ポインター オブジェクトを取得する方法を示しています。これを使用するには、抽出するオブジェクトの正確な型を知っている必要があります (つまり、指定した型は、含まれているファンクター/ポインターtypeidの型と一致する必要があります)。typeid

#include <cstdio>
#include <functional>

using std::printf;
using std::tr1::function;

int main(int, char**);
static function<int (int, char**)> main_func(&main);

int
main(int argc, char** argv)
{
    printf("%p == %p\n", *main_func.target<int (*)(int, char**)>(), &main);
    return 0;
}
于 2008-09-18T02:53:42.723 に答える
0

どうですか

map<key-type, function<void (int)> > listeners;
于 2008-09-18T03:16:24.383 に答える
0

私は同様の問題を抱えていて、それに対する解決策を見つけました。C++0x の機能をいくつか使用しましたが、便宜上、これらは必須の部分ではありません。こちらをご覧ください:
>メッセージング システム: コールバックは何でもかまいません

于 2011-07-31T16:05:52.433 に答える