2009-03-03 66 views
1

我正在尋找一個Python PubSub庫的C++替代品,其中我沒有連接信號插槽或左右,而是可以註冊一個特殊類型的消息,而不知道可以發送的對象它。Python的PubSub /觀察員模式的C + +?

+0

我認爲你需要充實一點。這是一個進程間需求還是隻是一個進程內的對象?給我們一些你需要解決的問題的背景。你已經提到Observer,它是這類問題的明顯設計模式。 – Jackson 2009-03-03 12:53:19

+0

我有一個GUI應用程序,數據存儲在分層次的有序類。當發生錯誤時,我想引發一個包含錯誤消息和代碼的事件。現在我想要父數據類_and_ GUI對這個事件做出反應,但是數據類應該沒有關於GUI的知識,反之亦然。 – Andre 2009-03-04 10:31:52

回答

2

也許你誤解了什麼信號和插槽。使用信號和插槽,您不必知道誰發送信號。您的「客戶」類只聲明插槽,外部管理員可以將信號連接到它們。

我建議你看看Qt。這是一個驚人的跨平臺庫,不僅僅是GUI支持。它可以方便有效地實現您可以使用的信號和插槽。

這些天它也被授權使用LGPL(除了GPL和商業版),所以您可以將它用於任何目的。

重新澄清您的評論,爲什麼不引發錯誤的例外?父母可以通知GUI,或者GUI可以註冊父母發出的信號。這樣父母也不必知道GUI。

+0

Qt不是公司政策的選擇原因。但是用你的方法,我需要一個知道事件發送者和接收者的經理,但我並不總是這種知識。另見對最初問題的評論。 – Andre 2009-03-04 10:35:31

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. 

所以..這是那種你正在尋找的東西?

請參閱herehere以獲取大部分此代碼的來源。

編輯:boost :: signal庫也可能做你想做的。

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