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

6 199 0
O''''Reilly Network For Information About''''s Book part 97 pps

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

Thông tin tài liệu

that the guard detected a problem can be insufficient; a slot may need to know the current temperature. Although both the guard (the signal) and the slot could access the temperature from a common sensor, it may be simplest to have the guard pass the current temperature to the slot when invoking it. As another example, consider when slots are connected to several signals: The slots will most likely need to know which signal invoked it. There are myriad use cases that require passing some information from signal to slot. The arguments that slots expect are part of a signal's declaration. The first argument to the signal class template is the signature for invoking the signal, and this signature is also used for the connected slots when the signal calls them. If we want the argument to be modifiable, we make sure that it is passed by non-const reference or pass a pointer, else we can pass it by value or reference to const. Note that besides the obvious difference that the original argument is either modifiable or not, this also has implications for the types of acceptable arguments to the signal itself and to the slot typeswhen the signal expects an argument by value or as reference to const, types that are implicitly convertible to the argument's type can be used to emit a signal. Likewise for slotsif the slot accepts its arguments by value or reference to const, this means that implicit conversion to that type from the actual argument type of the signal is allowed. We'll see more of this soon, as we consider how to properly pass arguments when signaling. Consider an automatic parking lot guard, which receives notifications as soon as a car enters or leaves the parking lot. It needs to know something unique about the carfor example, the car's registration number, so it can track the coming and going of each. (That would also permit the system to know just how ridiculous a sum to charge the owner according to the elapsed time.) The guard should also have a signal of its own, to be able to trigger an alarm when someone is trying to cheat. There needs to be a few security guards to listen to that signal, which we'll model using a class called security_guard. Finally, let's also add a gate class, which contains a signal that is signaled whenever a car enters or leaves the parking lot. (The parking_lot_guard will definitely be interested in knowing about this.) Let's look first at the declaration for the parking_lot_guard. class parking_lot_guard { typedef boost::signal<void (const std::string&)> alarm_type; typedef alarm_type::slot_type slot_type; boost::shared_ptr<alarm_type> alarm_; typedef std::vector<std::string> cars; typedef cars::iterator iterator; boost::shared_ptr<cars> cars_; public: parking_lot_guard(); boost::signals::connection connect_to_alarm(const slot_type& a); void operator()(bool is_entering,const std::string& car_id); private: void enter(const std::string& car_id); void leave(const std::string& car_id); }; There are three especially important parts to look at closely here; first of these is the alarm, which is a boost::signal that returns void and takes a std::string (which will be the identifier of the car). The declaration of such a signal is worth looking at again. boost::signal<void (const std::string&)> It's just like a function declaration, only without a function name. When in doubt, try to remember that there's really nothing more to it than that! It is possible to connect to this signal from outside using the member function connect_to_alarm. (We'll address how and why we would want to sound this alarm when implementing this class.) The next thing to note is that both the alarm and the container of car identifiers (a std::vector containing std::strings) are stored in boost::shared_ptrs. The reason for this is that although we only intend to declare one instance of parking_lot_guard, there are going to be multiple copies of it; because this guard class will also be connected to other signals later on, which will create copies (Boost.Signals copies the slots, which is required for managing lifetime correctly); but we still want all of the data to be available, so we share it. We could have avoided copying altogetherfor example, by using pointers or externalizing the slot behavior from this classbut doing it this way nicely illuminates a trap that's easy to fall into. Finally, note that we have declared a function call operator, and the reason for this is that we are going to connect the parking_lot_guard to a signal in the gate class (that we have yet to declare). Let's turn our attention to the security_guard class. class security_guard { std::string name_; public: security_guard (const char* name); void do_whatever_it_takes_to_stop_that_car() const; void nah_dont_bother() const; void operator()(const std::string& car_id) const; }; The security_guards don't really do much. The class has a function call operator, which is used as a slot for alarms from the parking_lot_guard, and just two other functions: One is for trying to stop cars for which the alarm goes off, and the other is used to do nothing. This brings us to the gate class, which detects when cars arrive at the parking lot, and when they leave it. class gate { typedef boost::signal<void (bool,const std::string&)> signal_type; typedef signal_type::slot_type slot_type; signal_type enter_or_leave_; public: boost::signals::connection connect_to_gate(const slot_type& s); void enter(const std::string& car_id); void leave(const std::string& car_id); }; You'll note that the gate class contains a signal that is to be emitted when a car either enters or leaves the parking lot. There is a public member function (connect_to_gate) for connecting to this signal, and two more functions (enter and leave) that are to be called as cars come and go. Now that we know the players, it's time to take a stab at implementing them. Let's start with the gate class. class gate { typedef boost::signal<void (bool,const std::string&)> signal_type; typedef signal_type::slot_type slot_type; signal_type enter_or_leave_; public: boost::signals::connection connect_to_gate(const slot_type& s) { return enter_or_leave_.connect(s); } void enter(const std::string& car_id) { enter_or_leave_(true,car_id); } void leave(const std::string& car_id) { enter_or_leave_(false,car_id); } }; The implementation is trivial. Most of the work is forwarded to other objects. The function connect_to_gate simply forwards a call to connect for the signal enter_or_leave_. The function enter signals the signal, passing TRue (this means that the car is entering) and the identifier of the car. leave does the same thing, but passes false, indicating that the car is leaving. A simple class for a simple chore. The security_guard class isn't much more complicated. class security_guard { std::string name_; public: security_guard (const char* name) : name_(name) {} void do_whatever_it_takes_to_stop_that_car() const { std::cout << "Stop in the name of eh " << name_ << '\n'; } void nah_dont_bother() const { std::cout << name_ << " says: Man, that coffee tastes f i n e fine!\n"; } void operator()(const std::string& car_id) const { if (car_id.size() && car_id[0]=='N') do_whatever_it_takes_to_stop_that_car(); else nah_dont_bother(); } }; security_guards know their names, and they can decide whether to do something when the alarm goes off (if the car_id starts with the letter N, they spring into action). The function call operator is the slot function that is calledsecurity_guard objects are function objects, and adhere to the requirements for parking_lot_guard's alarm_type signal. Things get a little more complicated with parking_lot_guard, but not too much. class parking_lot_guard { typedef boost::signal<void (const std::string&)> alarm_type; typedef alarm_type::slot_type slot_type; boost::shared_ptr<alarm_type> alarm_; typedef std::vector<std::string> cars; typedef cars::iterator iterator; boost::shared_ptr<cars> cars_; public: parking_lot_guard() : alarm_(new alarm_type), cars_(new cars) {} boost::signals::connection connect_to_alarm(const slot_type& a) { return alarm_->connect(a); } void operator() (bool is_entering,const std::string& car_id) { if (is_entering) enter(car_id); else leave(car_id); } private: void enter(const std::string& car_id) { std::cout << "parking_lot_guard::enter(" << car_id << ")\n"; // If the car already exists here, sound the alarm if (std::binary_search(cars_->begin(),cars_->end(),car_id)) (*alarm_)(car_id); else // Insert the car_id cars_->insert( std::lower_bound( cars_->begin(), cars_->end(),car_id),car_id); } void leave(const std::string& car_id) { . passing some information from signal to slot. The arguments that slots expect are part of a signal's declaration. The first argument to the signal class template is the signature for invoking. function call operator, which is used as a slot for alarms from the parking_lot_guard, and just two other functions: One is for trying to stop cars for which the alarm goes off, and the other. The implementation is trivial. Most of the work is forwarded to other objects. The function connect_to_gate simply forwards a call to connect for the signal enter_or_leave_. The function enter

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

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan