Basic concepts -- requirements


introduction, concepts, components, examples, patterns, experience, conclusions, references
The problem addressed in the DejaVu project is essentially to provide support for the software engineering of (moderately complex) applications that require, apart from other functionality, multi-media interfaces and associative online help. Such support is primarily offered in the form of class libraries, most of which are written in C++. The language C++ was chosen for pragmatic reasons, since it allows for easily wrapping public domain software written in C in classes with a more user-friendly interface. (Another reason was that C++ seemed to be the right vehicle for programming assignments, with regard to its ever more widespread use.)

Application development generally encompasses a variety of programming tasks, including system-level software development (for example for networking or multimedia functionality), programming the user interface (including the definition of screen layout and the responsivity of the interface widgets to user actions), and the definition of (high-level) application-specific functionality. Each of these kinds of tasks may require a different approach and possibly a different application programming language. For example, the development of the user interface is often more conveniently done using a scripting language, to avoid the waiting times involved in compiling and linking. Similarly, defining knowledge-level application-specific functionality may benefit from the use of a declarative or logic programming language.

In our project, we decided from the start to support a multiparadigm approach to software development and consequently we had to define the mutual interaction between the various language paradigms, as for example the interaction between C++ and a scripting language, such as Tcl. Current scripting languages, including Python and Tcl, provide facilities for being embedded in C and C++, but extending these languages with functionality defined in C or C++ and employing the language from within C/C++ is rather cumbersome. What we need is a simple and generic mechanism to associate script commands with actions defined by the application and (inversely) a generic way to employ scripting facilities in the application.

Embedding script interpreters

The hush library provides a solution to the latter problem by defining a generic kit that may be used as the interface to any embedded interpreter. (Currently, however, Tcl is fully supported and Python only partially.) The public interface of the kit class looks as follows:
  interface kit {

	void eval(char* cmd);
	char* result();

	void action(char* name, handler* h);
   };
The function eval is used for evaluating (script) commands, and result may be used to communicate data back. The limitation of this approach, obviously, is that it is purely string based. In practice, however, this proves to be flexible and sufficiently powerful. The action function may be used to define new commands and associate it with functionality defined in handler objects, that will be introduced soforth.

Handler objects

The former problem, of extending the script language with functionality defined by the application, is (as already indicated above) addressed by defining a generic handler object class. Handler objects may be regarded as a generalization of callback functions, in the sense that they are activated whenever the corresponding script command is evaluated. The advantage of using objects for callbacks instead of functions, obviously, is that we no longer need type insecure casts, or static or global variables to pass information around. The public interface of the handler class looks as follows:
     interface handler : client {
  	int dispatch( event* e );   
to dispatch event

int operator()(); };
The dispatch function is called by the underlying system. (In effect, a standard callback function is used with the handler object as client data, which explains why the handler object inherits from the class client.) The dispatch function receives a pointer to a sytsem-defined event which encodes the information relevant for that particular callback. In its turn dispatch calls the application operator. Classes derived from handler need only redefine the operator() function. Information needed when activating a handler object must be provided when creating the object, or obtained from the event for which the handler is activated.

The use of handler objects is closely connected to the paradigm of event-driven computation. An event, conceptually speaking is an entity that is characterized by two significant moments, the moment of its creation and the moment of its activation, its occurrence Naturally, an event may be activated multiple times and even record a history of its activation, but the basic principle underlying the use of events is that all the information that is needed is stored at creation time and, subsequently, activation may proceed blindly.

User actions

Another use of handler objects (in hush) is for defining the actions that must be taken in response to user events, resulting from actions such as moving the mouse, or pressing a button, or selecting an entry from a menu. This is illustrated by the public interface of the generic widget class:
   interface widget : handler {
	...
	void bind( handler* h );
	void bind( char* user, handler* h );
	...
   };
The first member function bind may be used for installing a handler for the default bindings of the widget, whereas the second bind function is to be used for overriding any specific bindings. (Notice that the class widget is derived from handler class to allow the widget to be its own handler. In this way inheritance or the delegation to a separate handler object may be used to define the functionality of a widget.)

In addition to the widget class, the hush library also provides the class item, representing graphical items. Graphical items, however, are to be placed within a canvas widget, and may be tagged to allow for the groupwise manipulation of a collection of items, as for example moving them in response to dragging the mouse pointer.

User-defined events

User interface events occur in response to actions by the user. They are scheduled by the underlying window system, which invokes the handler whenever it is convenient or necessary. When getting used to event-driven computation, system designers and programmers may feel the need to have events at their disposal that may be scheduled at will, under programmer's control. It will come as no surprise that another use of handler objects is to allow for user-defined events. The public interface of the class event looks as follows:
   interface event : handler {
	operator()();
   };
Actual event classes are derived from the generic class event, and a scheduler is provided to activate events at the appropriate time. (In effect, we provide a fully functional discrete event simulation library, including facilities for generating random distributions and analysing the outcome of experiments.) Note that there is an important difference between user-defined events and system-defined events. System-defined events are delivered to the user by activating a handler callback. In contrast, user-defined events are (directly) activated by a scheduler. They contain, so to speak, their own handler.

Discussion

What benefits do we derive from employing handler objects. One advantage is that we have a uniform way to define the functionality of script commands, callbacks to user actions and programmer controlled events. Another, less apparent advantage, is that it allows us to incorporate a variety of functionality (including sound synthesis facilities, digital video and active documents) in a relatively straightforward fashion, of which we will give an illustration later.
introduction, concepts, components, examples, patterns, experience, conclusions, references