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,
Carl
Title: Signals & Slots for Library TR2

Signals & Slots for Library TR2


Document number: NXXXX=05-NNNN
Date: July 26, 2005
Project: Programming Language C++, Library Working Group
Authors: Murray Cumming <murrayc -at- murrayc.com>
   Douglas Gregor <doug.gregor -at- gmail.com>
   Carl Nygard <cjnygard -at- verizon.net>
Reply-to: One of us

Table of contents

(Fill in when we know the structure)

Motivation

Multi-target callbacks in general, uses, etc. We should have a simple example, maybe with some kind of diagram that shows the signal/slot connections in a small GUI application.

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.

Coalescing return values

Talk about Accumulators.

Tracking and automatic disconnection

Talk about trackable, why we want it, etc.

Existing practice

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.

libsigc++

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<> object
  • emit(Arg1,Arg2,...,ArgN) a signal to each of it's connected slot<> objects
  • note the return value of the last slot, or marshall all return values into a container, and return the result(s) back to the caller of emit()
  • make_slot() provides a slot<> object suitable for connection to another signal<> to provide signal-to-signal connection.
  • provide access to the list of slots, for slot reordering
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:

  • encapsulate a function call to either a ptr-to-function using sigc::ptr_fun(), or ptr-to-member-function, or function object using sigc::mem_fun()
  • pass the arguments Arg1..N to the function it is wrapping
  • pass the return value of the function back to the caller
sigc::connection
connection object encapsulates an existing signal/slot connection. The connection provides facilities to:

  • disconnect() the slot<> object from the signal<>
  • temporarily block() the signal<> from calling the slot<>
  • unblock() the slot<> to resume calling the slot<>
  • check whether a connection is really connected or not
sigc::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:

  • automatically disconnect associated signal<> or slot<> objects upon destruction.
  • check whether a connection is really connected or not

Boost.Signals

boost::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<> object
  • emit(Arg1,Arg2,...,ArgN) a signal to each of it's connected slot<> objects
  • note the return value of the last slot, or marshall all return values into a container, and return the result(s) back to the caller of emit()
  • group 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:

  • encapsulate a function call to either a ptr-to-function or function object. If using a ptr-to-member-function, boost::bind must be used to bind the class object to the ptr-to-member-function
  • pass the arguments Arg1..N to the function it is wrapping
  • pass the return value of the function back to the caller
boost::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:

  • automatically disconnect associated signal<> or slot<> objects upon destruction.

.NET delegates

tr1::function

Impact on the standard

Very little to say here, probably.

Proposed text

Unresolved Issues

Number of Arguments

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<> Grouping

Boost.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.

References


dgregor
Last modified: Tue Jul 26 15:43:05 EST 2005

cjnygard
Last modified: Tue Aug 2 13:06:14 EST 2005
* 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
* added  to Motivation, expecting rough treatment editorially


_______________________________________________
libsigc-list mailing list
libsigc-list@gnome.org
http://mail.gnome.org/mailman/listinfo/libsigc-list

Reply via email to