To illustrate our approach, look at the definition of the class kit:
class kit { public: kit( char* kind = "tcl" ); virtual int eval(char* cmd) { return _kit?_kit->eval(cmd):0; } ... protected: kit( kit* ); kit* _kit; };The constructor of kit indicates as a default a Tcl interpreter which is encapsulated by the class:
class tcl_kit : public kit { public: tcl_kit() : kit(0) { ... } ... };The constructor of kit assigns the tcl_kit instance to its _kit variable. In contrast, the tcl_kit initializes its (parent) _kit variable to zero. For the kit instance created by the user, calls are delegated to the tcl_kit instance. However, the user may still inherit from kit and selectively override its functions.
class event : public handler { public: event( event* x ); // expects x event event( ); // creates sim event // X event interface virtual int type() { return _event?_event->type():0; } virtual int x() { return _event?_event->x():0; } virtual int y() { return _event?_event->y():0; } // simulation interface virtual void timestamp(); virtual double timespent() { return _event?_event->timespent():0; } protected: event* -event; };The disadvantage of this approach, clearly, is that we need to offer a fat interface (which could in principle be factored using multiple inheritance, at the expense of additional classes).
The (public) constructor event::event(event*) is used to encapsulate events generated by the (X) window system. An advantage of this approach is instance may be accessed via both pointer and object references, without sacrificing dynamic binding. (To our taste, it is more natural to write e.x() than e->x() when accessing the x coordinate of the mouse pointer stored in an event instance.)
For events that are to be scheduled under programmer's control, a class must be specified inheriting from event (overriding the operator() activation function). In that case a simulation event is created containing the additional data structures needed for bookkeeping and scheduling.
class drawtool : public canvas { public: drawtool() : canvas() { } // to declare drawtool drawtool(char* path) : canvas() { initialize( path ); // constructs the components redirect( _canvas ); // delegate to _canvas component } int operator()() { // interface to Tcl if ( !strcmp("drawtool",argv[1]) return create(argc, argv); else return self()->eval( flatten(argc, argv) ); } private: void initialize( char* path ); int create( int argc, char* argv[] ); canvas* _canvas; };For completeness, the first constructor is only used to define the script command drawtool as in
tk->action("drawtool", new drawtool() );which may occur in main or prelude of the application class.
The call redirect(_canvas) in the regular constructor results in delegating both member function calls and script commands addressed at the drawtool widget to the canvas widget pointed to by _canvas, which is assumed to be created in initialize. The _canvas widget acts, so to speak, as the primary component of drawtool in that it receives all bindings and (configuration) commands. Actual redirection occurs by calling self(). The definition of widget::self() is simply:
widget* self() { return _self?_self->self():this; }In other words, it checks the private _self variable of the widget, which may be given a value by widget::_redirect, until _self is undefined (zero), in which case the this pointer is returned. In this way primary widget components can be embedded at arbitrary depth within a composite widget, without any need to adapt the interface of the embedding (composite) widget.
Those well-versed in design patterns will recognize the Decorator
patterns (as applied in the Interviews MonoGlyph class