Python 的 C++ 的 PubSub/观察者模式?

发布于 2024-07-14 14:23:35 字数 80 浏览 6 评论 0原文

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

i'm looking for a C++ replacement of the Python PubSub Library in which i don't have to connect a signal with a slot or so, but instead can register for a special Kind of messages, without knowing the object which can send it.

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

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

发布评论

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

评论(3

梦醒时光 2024-07-21 14:23:35

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

我建议你看看Qt。 它是一个令人惊叹的跨平台库,不仅仅提供 GUI 支持。 它具有方便高效的信号和槽实现可供您使用。

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

关于您的澄清评论,为什么不针对错误提出例外? 父母可以通知GUI,或者GUI可以注册父母发出的信号。 这样家长也不必了解 GUI。

Perhaps you misunderstand what signals and slots are. With signals and slots you don't have to know who sends signals. Your "client" class just declares slots, and an outside manager can connect signals to them.

I recommend you to check out Qt. It's an amazing cross-platform library with much more than just GUI support. It has a convenient and efficient implementation of signals and slots which you can use.

These days it's also licensed with LGPL (in addition to GPL and commercial), so you can use it for practically any purpose.

Re your clarification comment, why not raise an exception for the error? The parent can notify the GUI, or alternatively the GUI can register for a signal the parent emits. This way the parent also doesn't have to know about the GUI.

围归者 2024-07-21 14:23:35

你可以使用 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 库也可能做你想做的事。

Can you use the boost libraries? If so then combining the function and bind libraries allows you to do the following. You may be able to do the same using the tr1 functionality if your compiler supports it.

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

So when you run this you get:

play
Started playing
stop

Press any key to continue.

So.. is this the kind of thing you are looking for?

See here and here for the source of most of this code.

EDIT: The boost::signal library might also do what you want.

情徒 2024-07-21 14:23:35

为什么不直接实施一个呢? 这不是一个复杂的模式(好吧,取决于你真正想要的)。 不管怎样,我前段时间已经实现了一个快速而肮脏的方法。 它没有经过优化、同步且单线程。 我希望你能用它来制作你自己的。

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

Why don't you just implement one? It's not a complicated pattern (well, depending what you really want). Anyway, I already implemented a quick and dirty one some time ago. It is not optimized, synchronous and single threaded. I hope you can use it to make your own.

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