I. Overview
I believe that when you hear the term "event processing", you will think of the GUI: click a button and the associated functions will be executed. Click itself is an event, and the function is the corresponding event processor.
Of course, this mode is not limited to Gui. In general, any object can call special functions based on specific events. The boost. Signals library introduced in this chapter provides a simple method to apply this mode in C ++.
Strictly speaking, the boost. the function library can also be used for event processing. However, boost. function and boost. A major difference between signals is boost. signals can associate more than one event processor to a single event, while functions can only be one-to-one. Therefore, boost. signals can better support event-driven development. When event processing is required, it should be the first choice.
Ii. Signal Signals
Although the library name seems a bit misleading at first glance, it is not actually the case, boost. the Mode Implemented by signals is named 'signal to slot' (the concept of slot has also been seen in QT, see the previous article). It is based on the following concepts: when the corresponding signal is sent, the connected slots are executed. In principle, you can replace the word "signal" and "slot" with "Event" and "event processor" respectively '. However, because the signal can be sent at any given time, this concept gives up the 'event' name.
Therefore, boost. signals does not provide any classes similar to 'event. Instead, it provides a class named boost: signal, defined in boost/signal. HPP.
Boost. Signals defines other classes in the boost: Signals namespace. Because boost: signal is the most commonly used class, it is located in the namespace boost.
#include <boost/signal.hpp> #include <iostream> void func() { std::cout << "Hello, world!" << std::endl; } int main() { boost::signal<void ()> s; s.connect(func); s(); }
Boost: signal is actually implemented as a template function with the signature of the function used as the event processor. This signature is also its template parameter. In this example, only functions with the signature void () can be successfully associated with signal S.
The function func () is associated with the signal s through the connect () method (why is it similar ). Since func () complies with the required void () signature, the association is established successfully. Therefore, when the signal S is triggered, func () is called.
The signal is triggered by calling s, just like a common function call. The signature of this function corresponds to the signature passed in as a template parameter: Because void () does not require any parameters, it is empty in brackets.
Calling s will trigger and execute the corresponding func () function. The previous single is associated with connect.
The same example can also be implemented using boost. function.
#include <boost/function.hpp> #include <iostream> void func() { std::cout << "Hello, world!" << std::endl; } int main() { boost::function<void ()> f; f = func; f(); }
Similar to the previous example, func () is associated with F. When F is called, func () is executed accordingly (). Boost. function is only applicable in this case, while boost. Signals provides many ways, such as associating multiple functions to a single signal. The example is as follows.
#include <boost/signal.hpp> #include <iostream> void func1() { std::cout << "Hello" << std::flush; } void func2() { std::cout << ", world!" << std::endl; } int main() { boost::signal<void ()> s; s.connect(func1); s.connect(func2); s(); }
Boost: signal can assign multiple functions to a single specific signal by repeatedly calling the connect () method. When this signal is triggered, these functions are executed in the order in which connect () was used for association.
In addition, the execution sequence can be explicitly specified by another overloaded version of the Connect () method. This overloaded version requires an int type value as an additional parameter.
#include <boost/signal.hpp> #include <iostream> void func1() { std::cout << "Hello" << std::flush; } void func2() { std::cout << ", world!" << std::endl; } int main() { boost::signal<void ()> s; s.connect(1, func2); s.connect(0, func1); s(); }
As in the previous example, func1 () is executed before func2.
To release the association between a function and a given signal, use the disconnect () method.
#include <boost/signal.hpp> #include <iostream> void func1() { std::cout << "Hello" << std::endl; } void func2() { std::cout << ", world!" << std::endl; } int main() { boost::signal<void ()> s; s.connect(func1); s.connect(func2); s.disconnect(func2); s(); }
In this example, only hello is output, because the association with func2 () has been released before the trigger signal.
In addition to connect () and disconnect (), boost: signal provides several methods.
#include <boost/signal.hpp> #include <iostream> void func1() { std::cout << "Hello" << std::flush; } void func2() { std::cout << ", world!" << std::endl; } int main() { boost::signal<void ()> s; s.connect(func1); s.connect(func2); std::cout << s.num_slots() << std::endl; if (!s.empty()) s(); s.disconnect_all_slots(); }
Num_slots () returns the number of associated functions. If no function is associated, num_slots () returns 0. In this particular case, you can use the empty () method instead. What the disconnect_all_slots () method does is actually expressed by its name: release all existing associations.
After reading how functions are associated with signals and figuring out what happens when a signal is triggered, another question is: where are the return values of these functions? The following example answers this question.
#include <boost/signal.hpp> #include <iostream> int func1() { return 1; } int func2() { return 2; } int main() { boost::signal<int ()> s; s.connect(func1); s.connect(func2); std::cout << s() << std::endl; }
Both func1 () and func2 () have int-type return values.STwo return values are processed and written to the standard output stream. So what exactly will happen?
The above example actually writes 2 to the standard output stream. Both return values areSReceived correctly, but other values except the last value will be ignored. By default, only the last return value is returned in all associated functions.
You can customize a signal to process each returned value accordingly. Therefore, we need to pass a thing called combiner as the second parameter to boost: signal.
#include <boost/signal.hpp> #include <iostream> #include <algorithm> int func1() { return 1; } int func2() { return 2; } template <typename T> struct min_element { typedef T result_type; template <typename InputIterator> T operator()(InputIterator first, InputIterator last) const { return *std::min_element(first, last); } }; int main() { boost::signal<int (), min_element<int> > s; s.connect(func1); s.connect(func2); std::cout << s() << std::endl; }