2008-09-18 82 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来解决你原来的问题 - 创建一个事件系统 - 而不是试图推出你自己的。

相关问题