Sourceforge has been impossible for me to get through to, so in the absence of being able to checkin to CVS, I include the draft doc with some added text. Mostly a summary of the existing practice, grabbed from existing docs. Also some issues noted, and a rough first attempt at the Motivation. I'm a better editor than writer, I think, so suggestions welcome. Please notify me if Sourceforge stabilizes, in case it's a problem on my end. Regards, CarlTitle: Signals & Slots for Library TR2
The Publisher/Subscriber idiom is well known in OOP circles for its ability to allow communication between objects without inducing tight coupling between said objects. This idiom is also generally known as a Callback system, and has been implemented in various flavors by many different libraries. This TR2 Proposal describes an implementation for a type-safe Publisher/Subscriber library.
One example where this idiom is well suited is in the design of a GUI library. The GUI library provides widgets on screen for user interaction. In order for a library client to respond to user input, it must have some way of receiving and acting on user-generated GUI events.
Many possibilities exist, for example one could require user code to derive each widget to specialize the behavior. For example:
class TextWidget;
class MyDateEntry : public TextWidget {
// override the text entry function
void TextEntry(char c) {
if(c == SPECIAL_CHAR){
// Do something special
}else{
TextWidget::TextEntry()
}
}
// Many other overrides
};
This method introduces tight coupling between the client code and the GUI widget classes. As such, it becomes very brittle, and further development of the GUI is strongly confined by existing implementation details.
In contrast, Publisher/Subscriber system provides the GUI library a way to communicate with the GUI library client without inducing strong coupling. The Publisher/Subscriber library provides a way to abstract the events to the basic function signature, and allows any client to subscribe to a Publisher as long as they meet the function signature requirements. Through the use of an intelligent Subscriber proxy object, ptr-to-function, ptr-to-member-function, and function-object are all opaque to the Publisher/Subscriber mechanism. For example:
class TextWidget;
class MyDateEntry {
void TextEntry(char c) {
// do special stuff here
}
void Init() {
TextWidget* widg = GrabOrCreateWidget();
widg->text_entry_signal()->
connect(std::subscriber(*this, &MyDateEntry::TextEntry));
}
};
This design reduces the amount of coupling between
TextWidget
and MyDateEntry
classes and limits
it to the definition of the TextWidget::*_signal()
Publisher objects defined by TextWidget
.
There have been a number of implementations of a Signal/Slot
library, notably the Callback library by Rich Hickey, Qt's addition of
signal/slot as C++ "keywords", libsigc++2
by Murray
Cumming and Martin Schulze, Boost.Signals by Douglas Gregor. We'll
summarize best existing practice by looking at
libsigc++2
, Boost.Signals, as well as look at the
commonalities with .NET delegates, and the tr1::function
class.
libsigc++2
and Boost.Signals are implemented very
similarly, having both grown out of the earlier
libsigc++1.2
library written by Karl Nelson and Tero
Pulkkinen. The major concepts of these two libraries are:
signal<>
object which acts as a publisher to
provide messages (really function calls) to a list of subscribers
(slot<>
objects)
slot<>
object which provides an interface to a function,
whether it be a member function, ptr-to-function, or function object.
The slot<>
provides the abstraction to the basic
function signature -- return value and argument list.
connection
object which represents a signal/slot
registration, allowing the programmer to disconnect a
slot<>
from a signal<>
trackable
base class, which provides the mechanism
for automatic signal/slot disconnection when the object is destructed.
sigc::signal<R,Arg1,Arg2,...,ArgN>
signal<>
object is templatized in terms of a return value
type R, and an arbitrary number of argument types
Arg1..N
. The signal<>
object provides
facilities to:
connect()
a slot<>
objectemit(Arg1,Arg2,...,ArgN) a signal to each of it's
connected slot<>
objects
emit()
make_slot()
provides a slot<>
object
suitable for connection to another signal<>
to provide
signal-to-signal connection.sigc::slot<R,Arg1,Arg2,...,ArgN>
slot<>
object is templatized in terms of a return value
type R, and an arbitrary number of argument types
Arg1..N
. The slot<>
object provides
facilities to:
sigc::ptr_fun()
, or ptr-to-member-function, or function
object using sigc::mem_fun()
Arg1..N
to the function it is wrappingsigc::connection
connection
object encapsulates an existing signal/slot
connection. The connection
provides facilities to:
disconnect()
the slot<>
object from the
signal<>
signal<>
from
calling the slot<>
slot<>
to resume
calling the slot<>
connection
is really connected or notsigc::trackable
trackable
object provides a base class for objects which
either contain signal<>
objects or are connected via
slot<>
objects. The trackable
object
provides facilities to:
signal<>
or
slot<>
objects upon destruction.connection
is really connected or notboost::signals::signal<R (Arg1,Arg2,...,ArgN)>
signal<>
object is templatized in terms of a function
signature with an arbitrary number of argument types
Arg1..N
. The signal<>
object provides
facilities to:
connect()
a slot<>
objectemit(Arg1,Arg2,...,ArgN)
a signal to each of it's
connected slot<>
objectsemit()
slot<>
objects in logical groups for ordering
slot<>
boost::signals::slot<R (Arg1,Arg2,...,ArgN)>
slot<>
object is templatized in terms of a function
signature with an arbitrary number of argument types
Arg1..N
. The slot<>
object provides
facilities to:
boost::bind
must be used to bind the class object to the
ptr-to-member-functionArg1..N
to the function it is wrappingboost::signals::connection
connection
object encapsulates an existing signal/slot
connection. The connection
provides facilities to:
disconnect()
the slot<>
object from the
signal<>
boost::signals::trackable
trackable
object provides a base class for objects which
either contain signal<>
objects or are connected via
slot<>
objects. The trackable
object
provides facilities to:
signal<>
or
slot<>
objects upon destruction.tr1::function
The number of arguments allowed to a signal will be fixed to some arbitrary number. Given that someone somewhere will find that limit inadequate, no matter what limit is chosen, some easy mechanism might be chosen to allow the user to define a signal/slot combination with the desired number of arguments.
libsigc++2
provides
this capability in the form of m4 macros which can be processed into a
header file form.
Boost.Signals implements the signal/slot objects in terms of preprocessor macros, allowing easy customization.
Slot<>
GroupingBoost.Signals provides a mechanism for grouping slots in some
logical orderly fashion. It has been shown in the Boost.Signals
implementation that this feature causes pain in the implementation,
and the author has expressed a desire to drop this feature from the
TR2 proposal. Perhaps there is a way to build a grouping mechanism in
"user space" code, utilizing a signal-to-signal connection capability
as is provided in libsigc++2
.
* added Existing Practice for libsigc++2 and Boost.Signals, with some commentary * added section for Unresolved Issues, with entries for number of args and grouping * addedto Motivation, expecting rough treatment editorially