
What is this article about
Today I will talk about the Boost Signals library - about signals, slots, connections, and how to use them.
A signal is a data type that can store several callback functions in itself and call them.
The slot is, accordingly, functions connected to the signal.
As already mentioned, several functions can be connected to one signal, and when a signal is called, the connected functions are called in the order they are connected.
A similar system is sometimes called event-based, then signals are events, and slots are handlers subscribed to certain events.
')
Simple example
Let's say we do a UI for the game. We will have a lot of buttons in the game, and each button, when pressed, will perform certain actions. And at the same time, I would like all the buttons to belong to the same type of Button - that is, it is necessary to separate the button from the code executed by clicking on it. Just for this separation and need signals.
Announcing the signal is very simple. Declare it as a member of the class:
#include "boost/signals.hpp" class Button { public: boost::signal<void()> OnPressed; // };
Here we create a signal that takes no parameters and does not return values.
Now we can connect a slot to this signal. The easiest way to use the function as a slot:
void FunctionSlot() { std::cout<<"FunctionSlot called"<<std::endl; } ... Button mainButton; mainButton.OnPressed.connect(&FunctionSlot);
In addition to the function, you can also connect a function object:
struct FunctionObjectSlot { void operator()() { std::cout<<"FunctionObjectSlot called"<<std::endl; } }; ...
Sometimes, if the code is very small, it is more convenient to write an anonymous function and immediately connect it:
If you need to call the object's method, you can also connect it using the syntax
boost :: bind :
#include "boost/bind.hpp" class MethodSlotClass { public: void MethodSlot() { std::cout<<"MethodSlot is called"<<std::endl; } }; ... MethodSlotClass methodSlotObject;
About Boost Bind, I probably will write a separate article.
Thus, we connected several slots to the signal at once. In order to “send” a signal, you must call the parenthesis operator () for the signal:
mainButton.OnPressed();
In this case the slots will be called in the order of their connection. In our case, the output will be as follows:
FunctionSlot called
FunctionObjectSlot called
Anonymous function is called
MethodSlot is called
Signals with parameters
Signals may contain parameters. Here is an example slot declaration that contains parameters:
boost::signal<void(int, int)> SelectCell;
In this case, obviously, the functions must be with parameters:
void OnPlayerSelectCell(int x, int y) { std::cout<<"Player selected cell: "<<x<<", "<<y<<std::endl; }
Signals returning objects
Signals can return objects. One subtlety is connected with this - if several slots are caused, then in fact, several objects are returned, aren’t they? But the signal, in turn, can return only one object. By default, the signal returns the object that was received from the last slot. However, we can transmit our own “aggregator” to the signal, which will group the returned objects into one.
I, of course, did not encounter a situation where I need to return values ​​from a signal, but oh well. Suppose, in our case, the signal causes slots that return strings, and the signal must return a string glued from these strings.
Such an aggregator is written simply - it is necessary to create a structure with template-based operator "brackets", which takes iterators as parameters:
struct Sum { template<typename InputIterator> std::string operator()(InputIterator first, InputIterator last) const {
Turn off signals
In order to disconnect all signals from the slot, you should call the disconnect_all_slots method.
In order to manage a separate slot, you will have to create a separate object like boost :: connection when connecting a slot.
Examples:
Call signaling order
I do not know when this can be useful, but when connecting slots, you can specify the order of the call:
mainButton.OnPressed.connect(1, &FunctionSlot); mainButton.OnPressed.connect(0, FunctionObjectSlot()); mainButton.OnPressed();
Conclusion
Signals and slots are very convenient in the case when you need to reduce the connectivity of various objects. Earlier, in order to call some objects from others, I passed pointers to one objects to other objects, and this caused cyclic references and turned my code into a mess. Now I use signals that allow us to stretch thin “bridges” between independent objects, and this greatly reduced the connectivity of my code. Use signals and slots for health!
References:
www.boost.org/doc/libs/1_53_0/doc/html/signals.html