1

我正在寻找 Python PubSub 库的 C++ 替代品,在该库中我不必将信号与插槽左右连接,而是可以注册一种特殊的消息,而不知道可以发送它的对象。

4

3 回答 3

2

也许你误解了什么是信号和插槽。使用信号和插槽,您不必知道谁发送信号。您的“客户端”类只声明插槽,外部管理器可以将信号连接到它们。

我建议您查看 Qt。这是一个了不起的跨平台库,不仅仅支持 GUI。它有一个方便有效的信号和槽实现,你可以使用它。

如今,它还获得了 LGPL 的许可(除了 GPL 和商业),因此您几乎可以将它用于任何目的。

重新澄清您的评论,为什么不对错误提出异常?父母可以通知 GUI,或者 GUI 可以注册父母发出的信号。这样,父母也不必了解 GUI。

于 2009-03-03T18:51:31.507 回答
2

你可以使用boost库吗?如果是这样,那么结合函数和绑定库允许您执行以下操作。如果您的编译器支持,您可以使用 tr1 功能执行相同的操作。

#include <iostream>
#include <list>
#include <boost/function.hpp>
#include <boost/bind.hpp>

typedef boost::function< void() > EVENT_T ;

template<typename F>
class Subject
{
    public:
        virtual void attach ( F o )
        {
            obs_.push_back ( o );
        }

        virtual void notify()
        {
            for ( typename std::list<F>::iterator i = obs_.begin(); i != obs_.end(); ++i )
                ( *i ) ();
        }

    private:
        std::list<F> obs_;
} ;

class Button : public Subject<EVENT_T>
{
    public:
        void onClick()
        {
            notify() ;
        };
};

class Player
{
    public:

        void play()
        {
            std::cout << "play" << std::endl ;
        }
        void stop()
        {
            std::cout << "stop" << std::endl ;
        }

};

class Display
{
    public:
        void started()
        {
            std::cout << "Started playing" << std::endl ;
        }
};

Button playButton ;
Button stopButton ;
Player thePlayer;
Display theDisplay ;

int main ( int argc, char **argv )
{
    playButton.attach ( boost::bind ( &Player::play, &thePlayer ) );
    playButton.attach ( boost::bind ( &Display::started, &theDisplay ) );
    stopButton.attach ( boost::bind ( &Player::stop, &thePlayer ) );

    playButton.onClick() ;
    stopButton.onClick() ;
    return 0;
}

因此,当您运行它时,您会得到:

play
Started playing
stop

Press any key to continue.

所以..这是你要找的那种东西吗?

有关大部分代码的来源,请参见此处此处。

编辑: boost::signal 库也可能做你想做的事。

于 2009-03-05T11:56:27.653 回答
2

你为什么不只实施一个?这不是一个复杂的模式(好吧,取决于你真正想要的)。无论如何,我前段时间已经实现了一个快速而肮脏的。它没有经过优化、同步和单线程。我希望你可以用它来制作你自己的。

#include <vector>
#include <iostream>
#include <algorithm>

template<typename MESSAGE> class Topic;
class Subscriber;

class TopicBase
{
    friend class Subscriber;
private:
    virtual void RemoveSubscriber(Subscriber* subscriber)=0;
};

template<typename MESSAGE>
class Topic : public TopicBase
{
    friend class Subscriber;
private:
    class Callable
    {
    public:
        Callable(Subscriber* subscriber, void (Subscriber::*method)(const MESSAGE&))
            :m_subscriber(subscriber)
            ,m_method(method)
        {
        }
        void operator()(const MESSAGE& message)
        {
            (m_subscriber->*m_method)(message);
        }
        bool operator==(const Callable& other) const
        {
            return m_subscriber == other.m_subscriber && m_method == other.m_method;
        }
    public:
        Subscriber* m_subscriber;
        void (Subscriber::*m_method)(const MESSAGE&);
    };
public:
    ~Topic()
    {
        //unregister each subscriber
        for(std::vector<Callable>::iterator i = m_subscribers.begin(); i != m_subscribers.end(); i++)
        {
            std::vector<TopicBase*>& topics  = i->m_subscriber->m_topics;
            for(std::vector<TopicBase*>::iterator ti = topics.begin();;)
            {
                ti = std::find(ti, topics.end(), this);
                if(ti == topics.end()) break;
                ti = topics.erase(ti);
            }
        }
    }
    void SendMessage(const MESSAGE& message)
    {
        for(std::vector<Callable>::iterator i = m_subscribers.begin(); i != m_subscribers.end(); i++)
        {
            (*i)(message);
        }
    }
private:
    void Subscribe(Subscriber* subscriber, void (Subscriber::*method)(const MESSAGE&))
    {
        m_subscribers.push_back(Callable(subscriber, method));
        subscriber->m_topics.push_back(this);
    }
    void Unsubscribe(Subscriber* subscriber, void (Subscriber::*method)(const MESSAGE&))
    {
        std::vector<Callable>::iterator i = std::find(m_subscribers.begin(), m_subscribers.end(), Callable(subscriber, method));
        if(i != m_subscribers.end())
        {
            m_subscribers.erase(i);
            subscriber->m_topics.erase(std::find(subscriber->m_topics.begin(), subscriber->m_topics.end(), this)); //should always find one
        }
    }
    virtual void RemoveSubscriber(Subscriber* subscriber)
    {
        for(std::vector<Callable>::iterator i = m_subscribers.begin() ; i != m_subscribers.end(); i++)
        {
            if(i->m_subscriber == subscriber)
            {
                m_subscribers.erase(i);
                break;
            }
        }
    }
private:
    std::vector<Callable> m_subscribers;
};


class Subscriber
{
    template<typename T> friend class Topic;
public:
    ~Subscriber()
    {
        for(std::vector<TopicBase*>::iterator i = m_topics.begin(); i !=m_topics.end(); i++)
        {
            (*i)->RemoveSubscriber(this);
        }
    }
protected:
    template<typename MESSAGE, typename SUBSCRIBER>
    void Subscribe(Topic<MESSAGE>& topic, void (SUBSCRIBER::*method)(const MESSAGE&))
    {
        topic.Subscribe(this, static_cast<void (Subscriber::*)(const MESSAGE&)>(method));
    }
    template<typename MESSAGE, typename SUBSCRIBER>
    void Unsubscribe(Topic<MESSAGE>& topic, void (SUBSCRIBER::*method)(const MESSAGE&))
    {
        topic.Unsubscribe(this, static_cast<void (Subscriber::*)(const MESSAGE&)>(method));
    }
private:
    std::vector<TopicBase*> m_topics;
};

// Test

Topic<int> Topic1;

class TestSubscriber1 : public Subscriber
{
public:
    TestSubscriber1()
    {
        Subscribe(Topic1, &TestSubscriber1::onTopic1);
    }
private:
    void onTopic1(const int& message)
    {
        std::cout<<"TestSubscriber1::onTopic1 "<<message<<std::endl;
    }
};

class TestSubscriber2 : public Subscriber
{
public:
    void Subscribe(Topic<const char*> &subscriber)
    {
        Subscriber::Subscribe(subscriber, &TestSubscriber2::onTopic);
    }
    void Unsubscribe(Topic<const char*> &subscriber)
    {
        Subscriber::Unsubscribe(subscriber, &TestSubscriber2::onTopic);
    }
private:
    void onTopic(const char* const& message)
    {
        std::cout<<"TestSubscriber1::onTopic1 "<<message<<std::endl;
    }
};


int main()
{
    Topic<const char*>* topic2 = new Topic<const char*>();
    {
        TestSubscriber1 testSubscriber1;
        Topic1.SendMessage(42);
        Topic1.SendMessage(5);
    }
    Topic1.SendMessage(256);

    TestSubscriber2 testSubscriber2;
    testSubscriber2.Subscribe(*topic2);
    topic2->SendMessage("owl");
    testSubscriber2.Unsubscribe(*topic2);
    topic2->SendMessage("owl");
    testSubscriber2.Subscribe(*topic2);
    delete topic2;

    return 0;
}
于 2009-03-06T18:20:00.687 回答