Learning c++ and trying to get familiar with some patterns. The signals2 doc clearly has a vast array of things I can do with slots and signals. What I don't understand is what types of applications (use cases) I should use it for.
I'm thinking along the lines of a state machine dispatching change events. Coming from a dynamically typed background (C#,Java etc) you'd use an event dispatcher or a static ref or a callback.
Are there difficulties in c++ with using cross-class callbacks? Is that essentially why signals2 exists?
One to the example cases is a document/view. How is this pattern better suited than say, using a vector of functions and calling each one in a loop, or say a lambda that calls state changes in registered listening class instances?
class Document { public: typedef boost::signals2::signal<void ()> signal_t; public: Document() {} /* Connect a slot to the signal which will be emitted whenever text is appended to the document. */ boost::signals2::connection connect(const signal_t::slot_type &subscriber) { return m_sig.connect(subscriber); } void append(const char* s) { m_text += s; m_sig(); } const std::string& getText() const { return m_text; } private: signal_t m_sig; std::string m_text; };
and
class TextView { public: TextView(Document& doc): m_document(doc) { m_connection = m_document.connect(boost::bind(&TextView::refresh, this)); } ~TextView() { m_connection.disconnect(); } void refresh() const { std::cout << "TextView: " << m_document.getText() << std::endl; } private: Document& m_document; boost::signals2::connection m_connection; };
The Boost. Signals library is an implementation of a managed signals and slots system. Signals represent callbacks with multiple targets, and are also called publishers or events in similar systems.
Signals2 are thread safe and can be used in multithreaded applications.
Boost.Signals2
is not just "an array of callbacks", it has a lot of added value. IMO, the most important points are:
connection
and scoped_connection
handles that allow disconnection without having direct access to the signal
. Note that this is the only way to disconnect incomparable slots, like boost::function
(or std::function
).Automatic slot lifespan tracking: a signal disconnects automatically from "expired" slots. Consider the situation when a slot is a binder referencing a non-copyable object managed by shared_ptr
s:
shared_ptr<listener> l = listener::create(); auto slot = bind(&listener::listen, l.get()); // we don't want aSignal_ to affect `listener` lifespan aSignal_.connect(your_signal_type::slot_type(slot).track(l)); // but do want to disconnect automatically when it gets destroyed
Certainly, one can re-implement all the above functionality on his own "using a vector of functions and calling each one in a loop" etc, but the question is how it would be better than Boost.Signals2
. Re-inventing the wheel is rarely a good idea.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With