比较 std::tr1::function<> 和 物体

发布于 2024-07-04 11:27:20 字数 576 浏览 4 评论 0 原文

我一直在尝试在 C++ 中实现类似 C# 的事件系统,并使用 tr1 函数模板来存储处理事件的函数。

我创建了一个向量,以便多个侦听器可以附加到此事件,即:

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

我希望能够从列表中删除处理程序以停止侦听器接收事件。

那么,如何在该列表中找到与给定侦听器对应的条目呢? 我可以测试列表中的“函数”对象是否引用特定函数吗?

谢谢!

编辑:研究了 boost::signal 方法后,它似乎可能是使用令牌系统实现的,正如你们中的一些人所建议的那样。 这里有一些相关信息。 观察者在附加到事件时保留一个“连接”对象,并且该连接对象用于在需要时断开连接。 所以看起来无论你使用 Boost 还是自己使用 tr1,基本原理都是一样的。 即它会有点笨拙:)

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.

I created a vector so that multiple listeners can be attached to this event, i.e.:

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. 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. i.e. it will be a bit clumsy :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

浪荡不羁 2024-07-11 11:27:20

关于什么

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

What about

map<key-type, function<void (int)> > listeners;
不必你懂 2024-07-11 11:27:20

我有类似的问题并找到了解决方案。 我使用了一些 C++0x 功能,但只是为了方便,它们不是必需的部分。 看看这里:
> 消息系统:回调可以是任何内容

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. Take a look here:
> Messaging system: Callbacks can be anything

寄人书 2024-07-11 11:27:20

提案(第 IIIb.1 节) )指出它们在任何方面都不具有可比性。 如果您向它们附加一些额外的信息,您可以轻松识别每个回调。 例如,如果您只是定义一个包装函数指针的结构,则可以删除它们(假设您具有插入的相同结构)。 您还可以向结构添加一些字段(例如客户端可以保留的自动生成的 guid)并与之进行比较。

The proposal (section IIIb.) states they will not be comparable in any way. 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.

攒眉千度 2024-07-11 11:27:20

如果您仅存储函数指针(而不存储与所需签名匹配的其他函子),这很容易(请参见下面的代码)。 但总的来说,正如其他发帖者所说,答案是否定的。 在这种情况下,您可能希望将函子作为值存储在哈希中,而键是用户在添加和删除时提供的内容。

下面的代码演示了如何获取要调用的仿函数/指针对象。 要使用它,您必须知道要提取的对象的确切类型(即,您指定的类型的 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;
}

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. To use it, you must know the exact type of the object to extract (i.e., the typeid of the type you specify must match the typeid of the contained functor/pointer).

#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;
}
残疾 2024-07-11 11:27:20

boost 函数文档中的 常见问题解答 #1似乎解决了你的问题 - 简单的答案是“不”。

FAQ #1 in the boost function documentation seems to address your question - and the easy answer is "no".

别挽留 2024-07-11 11:27:20

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

I don't know if you're locked into std C++ and tr1, but if you aren't, it seems like your problem could be completely avoided if you just used something like boost::signal and boost::bind to solve your original problem - creating an event system - instead of trying to roll your own.

山田美奈子 2024-07-11 11:27:20

好吧,你让我工作了。 困难的部分是尝试匹配 C# 事件的确切使用模式。 如果你跳过这个,有更简单的方法来完成你所要求的事情。 (我的同事 Jason 到处都使用了 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;
}

这是输出:

做活动
完成

做活动
..观察到!
完成

做活动
完成

做活动
..观察到!
..观察到!
完成
做活动
..观察到!
完成

Okay, you got me working. The hard part is trying to match the exact usage pattern of C# events. 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. Unfortunately, it doesn't allow you to pass parameters from the Subject to the 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

doing event
..observed!
done

doing event
done

doing event
..observed!
..observed!
done
doing event
..observed!
done

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文