简体   繁体   English

比较std :: tr1 :: function <>对象

[英]Comparing std::tr1::function<> objects

I've been trying to implement a C#-like event system in C++ with the tr1 function templates used to store a function that handles the event. 我一直在尝试用C ++实现类似C#的事件系统,其中tr1函数模板用于存储处理事件的函数。

I created a vector so that multiple listeners can be attached to this event, ie: 我创建了一个向量,以便可以将多个侦听器附加到此事件,即:

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

I'd like to be able to remove a handler from the list to stop a listener receiving events. 我希望能够从列表中删除处理程序以阻止侦听器接收事件。

So, how can I find the entry in this list that corresponds to a given listener? 那么,如何在此列表中找到与给定侦听器对应的条目? Can I test if a 'function' object in the list refers to a particular function? 我可以测试列表中的“函数”对象是否指向特定函数吗?

Thanks! 谢谢!

EDIT: Having looked into the boost::signal approach, it seems it's probably implemented using a token system as some of you have suggested. 编辑:看过boost :: signal方法,看起来它可能是使用令牌系统实现的,正如你们所建议的那样。 Here's some info on this . 这是关于此的一些信息 An observer retains a "Connection" object when they attach to an event, and this connection object is used to disconnect if needed. 观察者在附加到事件时保留“连接”对象,并且此连接对象用于在需要时断开连接。 So it looks like whether you use Boost or roll your own with tr1, the basic principle's the same. 所以看起来你是使用Boost还是使用tr1自己动手,基本原理是一样的。 ie it will be a bit clumsy :) 即它会有点笨拙:)

我不知道你是否被锁定在std C ++和tr1中,但如果你不是,那么如果你只是使用boost :: signal和boost :: bind来解决你的问题,你的问题似乎可以完全避免原始问题 - 创建一个事件系统 - 而不是试图自己推动。

Okay, you got me working. 好的,你让我工作了。 The hard part is trying to match the exact usage pattern of C# events. 困难的部分是试图匹配C#事件的确切使用模式。 If you skip that, there are MUCH easier ways to do what you're asking. 如果你跳过它,有很多更容易的方法来做你要求的。 (My co-worker Jason uses a Notifier object all over the place.) Anyway, here's the incredibly boring code which does what you want. (我的同事Jason在整个地方都使用了Notifier对象。)无论如何,这是令人难以置信的无聊代码,可以满足您的需求。 Unfortunately, it doesn't allow you to pass parameters from the Subject to the Observer. 不幸的是,它不允许您将参数从Subject传递给Observer。 To do that, you'd need to add even more smarts. 要做到这一点,你需要添加更多的智能。

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

And here's the output: 这是输出:

doing event 做事
done DONE

doing event 做事
..observed! ..观测到的!
done DONE

doing event 做事
done DONE

doing event 做事
..observed! ..观测到的!
..observed! ..观测到的!
done DONE
doing event 做事
..observed! ..观测到的!
done DONE

增强功能文档中的FAQ#1似乎解决了您的问题 - 简单的答案是“不”。

The proposal (section IIIb.) states they will not be comparable in any way. 提案 (第IIIb。节)规定它们不会以任何方式具有可比性。 If you attach some extra information to them, you can easily identify each callback. 如果您向它们附加一些额外信息,则可以轻松识别每个回调。 For instance, if you simply define a struct wrapping the function pointer, you can remove them (assuming you have the same struct you inserted). 例如,如果您只是定义一个包装函数指针的结构,您可以删除它们(假设您插入了相同的结构)。 You can also add some fields to the struct (like an automatically generated guid the client can hold on to) and compare against that. 您还可以向结构添加一些字段(如客户端可以保留的自动生成的guid)并与之进行比较。

I had a similar problem and found a solution to it. 我有类似的问题,并找到了解决方案。 I used some C++0x features, but only for convenience, they are not an essential part. 我使用了一些C ++ 0x功能,但只是为了方便,它们不是必不可少的部分。 Take a look here: 看看这里:
> Messaging system: Callbacks can be anything > 消息系统:回调可以是任何东西

If you are storing function pointers only (and not other functors that match the signature required), this is easy (see code below). 如果您只存储函数指针(而不是其他符合所需签名的仿函数),这很容易(参见下面的代码)。 But in general, the answer, like other posters have said, is no. 但总的来说,答案就像其他海报所说的那样,不是。 In that case, you probably want to store your functors in a hash, as values, with keys being something the user supplies on adding and removing. 在这种情况下,您可能希望将函数存储在散列中作为值,其中键是用户在添加和删除时提供的内容。

The code below demonstrates how to get the functor/pointer object that is to be called. 下面的代码演示了如何获取要调用的functor / pointer对象。 To use it, you must know the exact type of the object to extract (ie, the typeid of the type you specify must match the typeid of the contained functor/pointer). 要使用它,你必须知道对象的确切类型提取(即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;
}

关于什么

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM