1. Trang chủ
  2. » Công Nghệ Thông Tin

O''''Reilly Network For Information About''''s Book part 96 docx

5 239 0

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 5
Dung lượng 20,46 KB

Nội dung

Usage When faced with needing more than one piece of code in a program to handle a given event, typical solutions involve callbacks through function pointers, or directly coded dependencies between the subsystem that fires the event and the subsystems that need to handle it. Circular dependencies are a common result of such designs. Using Boost.Signals, you gain flexibility and decoupling. To start using the library, include the header "boost/signals.hpp". [2] The following example demonstrates the basic properties of signals and slots, including how to connect them and how to emit the signal. Note that a slot is something that you provide, either a function or a function object that is compatible with the function signature of the signal. In the following code, we create both a free function, my_first_slot, and a function object, my_second_slot; both are then connected to the signal that we create. #include <iostream> #include "boost/signals.hpp" void my_first_slot() { std::cout << "void my_first_slot()\n"; } class my_second_slot { public: void operator()() const { std::cout << "void my_second_slot::operator()() const\n"; } }; int main() { boost::signal<void ()> sig; sig.connect(&my_first_slot); sig.connect(my_second_slot()); std::cout << "Emitting a signal \n"; sig(); } We start by declaring a signal, which expects slots that return void and take no arguments. We then connect two compatible slot types to that signal. For one, we call connect with the address of the free function, my_first_slot. For the other, we default-construct an instance of the function object my_second_slot and pass it to connect. These connections mean that when we emit a signal (by invoking sig), the two slots will be called immediately. sig(); When running the program, the output will look something like this: Emitting a signal void my_first_slot() void my_second_slot::operator()() const However, the order of the last two lines is unspecified because slots belonging to the same group are invoked in an unspecified order. There is no way of telling which of our slots will be called first. Whenever the calling order of slots matters, you must put them into groups. Grouping Slots It is sometimes important to know that some slots are called before others, such as when the slots have side effects that other slots might depend upon. Groups is the name of the concept that supports such requirements. It is a signal template argument named Group that is, by default, int. The ordering of Groups is std::less<Group>, which translates to operator< for int. In other words, a slot belonging to group 0 is called before a slot in group 1, and so on. Note, however, that slots in the same group are called in an unspecified order. The only way to exactly control the order by which all slots are called is to put every slot into its own group. A slot is assigned to a group by passing a Group to signal::connect. The group to which a connected slot belongs cannot be changed; to change the group for a slot, it must be disconnected and then reconnected to the signal in the new group. As an example, consider two slots taking one argument of type int&; the first doubles the argument, and the second increases the current value by 3. Let's say that the correct semantics are that the value first be doubled, and then increased by 3. Without a specified order, we have no way of ensuring these semantics. Here's an approach that works on some systems, some of the time (typically when the moon is full and it's Monday or Wednesday). #include <iostream> #include "boost/signals.hpp" class double_slot { public: void operator()(int& i) const { i*=2; } }; class plus_slot { public: void operator()(int& i) const { i+=3; } }; int main() { boost::signal<void (int&)> sig; sig.connect(double_slot()); sig.connect(plus_slot()); int result=12; sig(result); std::cout << "The result is: " << result << '\n'; } When running this program, it might produce this output: The result is: 30 Or, it might produce this: The result is: 27 There's simply no way of guaranteeing the correct behavior without using groups. We need to ensure that double_slot is always called before plus_slot. That requires that we specify that double_slot belongs to a group that is ordered before plus_slot's group, like so: sig.connect(0,double_slot()); sig.connect(1,plus_slot()); This ensures that we'll get what we want (in this case, 27). Again, note that for slots belonging to the same group, the order with which they are called is unspecified. As soon as a specific ordering of slot invocation is required, make sure to express that using different groups. Note that the type of Groups is a template parameter to the class signal, which makes it possible to use, for example, std::string as the type. #include <iostream> #include <string> #include "boost/signals.hpp" class some_slot { std::string s_; public: some_slot(const std::string& s) : s_(s) {} void operator()() const { std::cout << s_ << '\n'; } }; int main() { boost::signal<void (), boost::last_value<void>,std::string> sig; some_slot s1("I must be called first, you see!"); some_slot s2("I don't care when you call me, not at all. \ It'll be after those belonging to groups, anyway."); some_slot s3("I'd like to be called second, please."); sig.connect(s2); sig.connect("Last group",s3); sig.connect("First group",s1); sig(); } First we define a slot type that prints a std::string to std::cout when it is invoked. Then, we get to the declaration of the signal. Because the Groups parameter comes after the Combiner type, we must specify that also (we'll just declare the default). We then set the Groups type to std::string. boost::signal<void (),boost::last_value<void>,std::string> sig; We accept the defaults for the rest of the template parameters. When connecting the slots s1, s2, and s3, the groups that we create are lexicographically ordered (because that's what std::less<std::string> does), so "First group" precedes "Last group". Note that because these string literals are implicitly convertible to std::string, we are allowed to pass them directly to the connect function of signal. Running the program tells us that we got it right. I must be called first, you see! I'd like to be called second, please. I don't care when you call me, not at all. It'll be after those belonging to groups, anyway. We could have opted for another ordering when declaring the signal typefor example, using std::greater. boost::signal<void (),boost::last_value<void>, std::string,std::greater<std::string> > sig; Had we used that in the example, the output would be I'd like to be called second, please. I must be called first, you see! I don't care when you call me. Of course, for this example, std::greater produces an ordering that leads to the wrong output, but that's another story. Groups are useful, even indispensable, but it's not always trivial to assign the correct group values, because connecting slots isn't necessarily performed from the same location in the code. It can thus be a problem to know which value should be used for a particular slot. Sometimes, this problem can be solved with disciplinethat is, commenting the code and making sure that everyone reads the commentsbut this only works when there aren't many places in the code where the values are assigned and when there are no lazy programmers. In other words, this approach doesn't work. Instead, you need a central source of group values that can be generated based upon some supplied value that is unique to each slot or, if the dependent slots know about each other, the slots could provide their own group value. Now that you know how to deal with issues of slot call ordering, let's take a look at different signatures for your signals. You often need to pass additional information along about important events in your systems. Signals with Arguments Often, there is additional data to be passed to a signal. For example, consider a temperature guard that reports drastic changes in the temperature. Just knowing . connecting slots isn't necessarily performed from the same location in the code. It can thus be a problem to know which value should be used for a particular slot. Sometimes, this problem. slot call ordering, let's take a look at different signatures for your signals. You often need to pass additional information along about important events in your systems. Signals with. to ensure that double_slot is always called before plus_slot. That requires that we specify that double_slot belongs to a group that is ordered before plus_slot's group, like so: sig.connect(0,double_slot());

Ngày đăng: 07/07/2014, 08:20

TỪ KHÓA LIÊN QUAN