2008-09-18 47 views
5

我一直試圖在C++中實現一個C#類事件系統,用於存儲處理事件的函數的tr1函數模板。比較std :: tr1 :: function <>對象

我創建使多個監聽器可以連接到這個事件,矢量即:

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

我希望能夠從列表中刪除處理程序停止監聽程序接收事件。

那麼,我如何才能在這個列表中找到與給定監聽器相對應的條目呢?我可以測試列表中的「函數」對象是否指向特定函數?

謝謝!

編輯:查看了boost :: signal方法,似乎它可能是使用令牌系統實現的,正如你們中的一些人所建議的那樣。 Here's some info on this。觀察者在附加到事件時保留「連接」對象,並且在需要時使用此連接對象斷開連接。所以它看起來像是使用Boost還是使用tr1滾動自己,基本原則是一樣的。即它會有點笨拙:)

回答

2

FAQ #1在助推功能文檔似乎解決您的問題 - 和簡單的答案是「不」。

1

proposal(第Ⅲb節)指出它們不會以任何方式進行比較。如果您附加一些額外的信息,您可以輕鬆識別每個回調。例如,如果你只是定義了一個包裝函數指針的結構體,你可以將它們移除(假設你有相同的結構體)。你也可以將一些字段添加到結構中(例如客戶端可以保留的自動生成的GUID)並與之進行比較。

+0

如果你要使用結構具有額外的信息,您不妨仿函數存儲爲哈希值,和GUID /什麼是哈希鍵。 :-) – 2008-09-18 02:55:32

+0

您不能將函子存儲爲散列值。你無法對他們做任何事情。將它們包裝在一個結構中(甚至是函子的一個單元結構)將允許你對它們做任何事情。這是故意的,他們不能比較/哈希/等沒有一些工作。 – hazzen 2008-09-18 16:32:38

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; 
} 
0

什麼

map<key-type, function<void (int)> > listeners; 
+0

我認爲散列(std :: tr1 :: unordered_map)是這類事情的更好選擇。除非按照註冊順序調用聽衆非常重要,在這種情況下,密鑰類型應該包含/是一個序列號。 – 2008-09-18 06:58:05

4

好了,你讓我的工作。困難的部分是試圖匹配C#事件的確切使用模式。如果你跳過這一點,有很多更簡單的方法來做你想問的問題。 (我的同事Jason在整個地方都使用了一個Notifier對象)。無論如何,這是令人難以置信的無聊代碼,它可以做你想做的事。不幸的是,它不允許你將參數從Subject傳遞給Observer。要做到這一點,你需要添加更多的智慧。

#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; 
} 

而這裏的輸出:

做事件

做事件
..observed!

做事件

做事件
..observed!
..觀看!
完成
做事件
..觀看!

8

,如果你鎖定STD C++和TR1我不知道,但如果你都沒有,好像你的問題可能如果你只是使用類似升壓完全避免::信號和boost :: bind來解決你原來的問題 - 創建一個事件系統 - 而不是試圖推出你自己的。

相關問題