If you've never been exposed to the signals and slots pattern before, it can be a little confusing. Simply put, a slot is a function or function object that is "listening" for a signal. A slot "listening" to a certain signal is said to be connected to that signal. When a signal is emitted, all slots that are connected to that signal are called (since they are all functions/function objects). If a signal is emitted to which no slots are connected, nothing happens.
Originally, GG used a very simple strategy for passing messages between windows/controls. The method was to call a Command() function, which passed to the callee an integer representing the type of message (button click, scroll, or whatever), an ID number associated with the calling control, and a single integer parameter. It could have been made more general and even less typesafe by replacing the int parameter with a void pointer, but in either case it suffers from two huge limitations. First, the type of message and the parameter must be known to both caller and callee; this means that, for example, the user must keep track of the integer value representing a button click message, and make sure that separate sections of code are using the same value to represent it. Second, any code that doesn't contain a control, but that wants to know about button pushes, scrolling scrollbars, etc., must deal with that control's parent (or break encapsulation). This creates a lot of chains of passed messages, and a large number of kludges to get messages to the sections of code that need them.
Now, each control emits a signal every time a significant event occurs. If no processing needs to be associated with such an event, its signal need not be connected to anything. Futhermore, no matter how many modules need to react to a certain signal, they can all be connected.
There are two types of connections between signals and slots. The first type of connection is between a signal and a slot, which may be a functor (which in turn may be a boost::bind object), a free function or a static member function or function object. Just call Connect(sig, slot). The second type is between a signal and a non-static member function. In this case, the call to Connect() requires a third parameter: a pointer to the object whose member function is to be called. For instance, if I have a class Foo and an object foo1 of class Foo, I cannot call foo1.bar() by simply knowing the address of bar(). I need to call Connect(sig, &Foo::bar, &foo1) so that the signal knows which object's bar() to call. Both versions of Connect() return a connection object. By keeping this connection object, you can later disconnect() the connection you made. In addition, signal-signal connections are possible. Calling Connect(sig1, sig2) forwards all sig1's signal emissions to sig2. This is provided as a convenience to avoid having to write dummy forwarding functions that do this.
To emit a signal from a signal object "sig", just use its function call operator ("operator()"), like this: "sig();"
If you kept the connection object returned by Connect(), a connection_obj.disconnect() call will disconnect the associated signal and slot. Also, consider this: what happens if a signal A is connected to a slot consisting of a member function of object B, B is deleted, and then A() is called? A segfault, unless the B's class inherits from boost::trackable, which auto-disconnects slots in its destructor. GG::Wnd is derived from boost::trackable, so all GG::Wnd-derived classes should handle such situations gracefully. See the boost tutorial below for details.
- See also:
- http://boost.org/doc/html/signals.html for a tutorial on other aspects of signals and slots; you can create connections that have scope, control the order of the invocation of multiple slots that are called by a signal, and combine the return values of such slots (take the first return value, the last, the average of all of them, etc.).
Generated on Wed Mar 26 14:35:42 2008 for GG by