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

O''''Reilly Network For Information About''''s Book part 99 pps

6 172 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 6
Dung lượng 22,53 KB

Nội dung

else std::cout << "At least one test failed. Aborting.\n"; } With the preceding code, there is no way that the code will ever know that one of the tests has failed. As you recall, the default combiner is boost::last_value, and it simply returns the value of the last slot call, which is the call to step2. Running the example as-is gives us this disappointing output: step0 is ok step1 is not ok. This won't do at all! step2 is ok All system tests clear This is clearly not the right result. We need a Combiner that stops processing when a slot returns false, and propagates that value back to the signal. A Combiner is nothing more than a function object with a couple of additional requirements. It must have a typedef called result_type, which is the return type of its function call operator. Furthermore, the function call operator must be parameterized on the iterator type with which it will be invoked. The Combiner that we need right now is quite simple, so it serves as a good example. class stop_on_failure { public: typedef bool result_type; template <typename InputIterator> bool operator()(InputIterator begin,InputIterator end) const { while (begin!=end) { if (!*begin) return false; ++begin; } return true; } }; Note the public typedef result_type, which is bool. The type of result_type doesn't necessarily relate to the return type of the slots. (When declaring the signal, you specify the signature of the slots and the arguments of the signal's function call operator. However, the return type of the Combiner determines the return type of the signal's function call operator. By default, this is the same as the return type of the slots, but it doesn't have to be the same.) stop_on_failure's function call operator, which is parameterized on a slot iterator type, iterates over the range of slots and calls each one; unless we encounter an error. For stop_on_failure, we don't want to continue calling the slots on an error return, so we test the return value for each call. If it is false, the function returns immediately, else it continues invoking the slots. To use stop_on_failure, we simply say so when declaring the signal type: boost::signal<bool (),stop_on_failure> sig; If we had used this in the bootstrap sequence from the previous example, the output would have been exactly as we had intended. step0 is ok step1 is not ok. This won't do at all! At least one test failed. Aborting. Another useful type of Combiner is one that returns the maximum or the minimum, of all of the values returned by the invoked slots. Many other interesting Combiners are possible, including one to save all results in a container. The (excellent) online documentation for this library has an example of just such a Combinerbe sure to read it! It's not every day you'll need to write your own Combiner class, but now and then doing so creates an especially elegant solution to an otherwise complicated problem. Signals Can Never Be Copied I have already mentioned that signals cannot be copied, but it's worth mentioning what one should do about classes that contain a signal. Must these classes be non-copyable too? No they don't have to be, but the copy constructor and the assignment operator needs to be implemented by hand. Because the signal class declares the copy constructor and the assignment operator private, a class aggregating signals has to implement the semantics that apply for it. One way to handle copies correctly is to use shared signals between instances of a class, which is what we did in the parking lot example. There, every instance of parking_lot_guard referred to the same signal by means of boost::shared_ptr. For other classes, it makes sense to simply default- construct the signal in the copy, because the copy semantics don't include the connection of the slots. There are also scenarios where copying of classes containing a signal doesn't make sense, in which one can rely on the non- copyable semantics of the contained signal to ensure that copying and assignment is verboten. To give a better view of what's going on here, consider the class some_class, defined here: class some_class { boost::signal<void (int)> some_signal; }; Given this class, the copy constructor and the assignment operator that the compiler generates isn't usable. If code is written that tries to use these, the compiler complains. For example, the following example tries to copy construct the some_class sc2 from sc1: int main() { some_class sc1; some_class sc2(sc1); } When compiling this program, the compiler-generated copy constructor tries to perform a member-by-member copy of the members of some_class. Because of the private copy constructor of signal, the compiler says something like this: c:/boost_cvs/boost/boost/noncopyable.hpp: In copy constructor ` boost::signals::detail::signal_base::signal_base(const boost::signals::detail::signal_base&)': c:/boost_cvs/boost/boost/noncopyable.hpp:27: error: ` boost::noncopyable::noncopyable( const boost::noncopyable&)' is private noncopyable_example.cpp:10: error: within this context So whatever your copying and assignment of classes containing signals need to do, make sure that need doesn't include copying of the signals! Managing Connections We have covered how to connect slots to signals, but we haven't yet seen how to disconnect them. There are many reasons to not leave a slot connected permanently to a signal. Until now, we've ignored it, but boost::signal::connect returns an instance of boost::signals::connection. Using this connection object, it is possible to disconnect a slot from the signal, as well as test whether the slot is currently connected to the signal. connections are a sort of handle to the actual link between the signal and the slot. Because the knowledge of the connection between a signal and a slot is tracked separately from both, a slot doesn't know if or when it is connected. If a slot won't be disconnected from a signal, it's typically fine to just ignore the connection returned by signal::connect. Also, a call to disconnect for the group to which a slot belongs, or a call to disconnect_all_slots will disconnect a slot without the need for the slot's connection. If it's important to be able to test whether a slot is still connected to a signal, there is no other way than by saving the connection and using it to query the signal. The connection class provides operator<, which makes it possible to store connections in the Standard Library containers. It also provides operator== for completeness. Finally, the class provides a swap member function that swaps the signal/slot connection knowledge of one connection with that of another. The following example demonstrates how to use the signals::connection class: #include <iostream> #include <string> #include "boost/signals.hpp" class some_slot_type { std::string s_; public: some_slot_type(const char* s) : s_(s) {} void operator()(const std::string& s) const { std::cout << s_ << ": " << s << '\n'; } }; int main() { boost::signal<void (const std::string&)> sig; some_slot_type sc1("sc1"); some_slot_type sc2("sc2"); boost::signals::connection c1=sig.connect(sc1); boost::signals::connection c2=sig.connect(sc2); // Comparison std::cout << "c1==c2: " << (c1==c2) << '\n'; std::cout << "c1<c2: " << (c1<c2) << '\n'; // Testing the connection if (c1.connected()) std::cout << "c1 is connected to a signal\n"; // Swapping and disconnecting sig("Hello there"); c1.swap(c2); sig("We've swapped the connections"); c1.disconnect(); sig("Disconnected c1, which referred to sc2 after the swap"); } There are two connection objects in the example, and we saw that they can be compared using operator< and operator==. The ordering relation that operator< implements is unspecified; it exists to support storing connections in the Standard Library containers. Equality through operator== is well defined, however. If two connections reference the exact same physical connection, they are equal. If two connections do not reference any connection, they are equal. No other pairs of connections are equal. In the example, we also disconnected a connection. c1.disconnect(); Although c1 originally refers to the connection for sc1, at the time of disconnecting it refers to sc2, because we swap the contents of the two connections using the member function swap. Disconnecting means that the slot no longer is notified when the signal is signaled. Here is the output from running the program: c1==c2: 0 c1<c2: 1 c1 is connected to a signal sc1: Hello there sc2: Hello there sc1: We've swapped the connections sc2: We've swapped the connections sc1: Disconnected c1, which referred to sc2 after the swap As you can see, the last invocation of the signal sig only invokes the slot sc1. Sometimes, the lifetime of the connection for a slot relates to a certain scope in the code. This is similar to any other resource that is required for a given scope, something that is handled using smart pointers or other mechanisms for scoping. Boost.Signals provides a scoped version for connections called scoped_connection. A scoped_connection makes sure that the connection is disconnected as the scoped_connection is destroyed. The constructor of scoped_connection takes a connection object as argument, which it assumes ownership of in a way. . one; unless we encounter an error. For stop_on_failure, we don't want to continue calling the slots on an error return, so we test the return value for each call. If it is false, the. signal::connect. Also, a call to disconnect for the group to which a slot belongs, or a call to disconnect_all_slots will disconnect a slot without the need for the slot's connection. If it's. Sometimes, the lifetime of the connection for a slot relates to a certain scope in the code. This is similar to any other resource that is required for a given scope, something that is handled

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