Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

Object-Oriented Program Construction


OCT92: OBJECT-ORIENTED PROGRAM CONSTRUCTION

Bill was formerly director of PC Labs. Currently he is with Nu-Mega Technologies, where he is working on a new version of Soft-ICE. You can reach him on CompuServe at 71561,2502.


Object-oriented programming brings us encapsulation, inheritance, polymorphism, and the promise of reusable software. Although OO languages make creation of building blocks easier, at present blocks are connected in the same fashion as in conventional programming languages. For example, application frameworks such as Microsoft's Foundation Classes (MFC) or Borland's ObjectWindows Library (OWL) provide numerous building blocks so you don't have to fabricate window classes from scratch. However, when you connect objects together, you must use the pointers and functions unique to a particular class.

Program construction consists of both component construction (class definition) and component interconnection. I've invented a concept which I call plugs to address the issue of component interconnection. Plugs provide a standard syntax and semantics for connecting disparate components. Plugs are an object class used in conjunction with conventional classes.

Plug objects work like their electrical counterparts. They are "keyed" so that, as in the physical counterpart, you can plug a keyboard connector into the back of a PC but not into the power connector. Likewise, the power connector can be plugged into a wall socket but not into the monitor's video connector. A plug class can provide a simple connection like a single wire or multiple connections with a single plug like a VGA cable.

The act of cabling together a PC and its components is analogous to building a simple computer program. The difference is that a conventional program makes the connections between its components using a variety of function calls and pointer assignments. The plug classes make this process consistent. For example, assume we have object classes defined for a power outlet, a PC, a monitor, and a keyboard. The process of connecting together these objects should be simple, as shown in Example 1(a).

Example 1: (a) The plugs approach to interconnection; (b) the conventional approach to interconnection.

  (a)

  void BuildAPC ()
  {

      PowerOutlet po1, po2 ;
      PC          pc ;
      Monitor     monitor ;

      po1 <= pc. PowerConnector ;
      po2 <= monitor.PowerConnector ;

      monitor.VideoConnector <= pc.VideoConnector ;

      monitor.TurnOn () ;
      pc.TurnOn () ;

  }

  (b)

  void BuildAPC ()
  {

      PowerOutlet po1, po2 ;
      PC          pc ;
      Monitor     monitor ;

      pc. PowerConnector.Connect ( & po1 ) ;
      monitor.PowerConnector.Connect ( & po2 ) ;

      monitor.VideoConnector.Connect ( & pc. VideoConnector ) ;

      monitor.TurnOn () ;
      pc.TurnOn () ;
  }

Example 1(a) makes a number of assumptions. First, the PowerOutlet objects are plugs, and the Monitor and PC objects both have two plugs: one for power and the other for video support. The <= method is used to connect two plugs of the appropriate type together. By definition, it does not matter which plug is on the left side of the operator. The TurnOn method is used to show how a set of connected objects might be put into operation.

But how might this be accomplished using conventional programs? Again we make some assumptions about the objects, so the code would look something like Example 1(b).

The Connect methods are used with pointer parameters. The effect might be similar, but implications are different: The use of pointers provides only a one-way link between the objects; plugs actually provide a two-way link. Also, the choice of Connect for a function name is arbitrary. Function names in dissimilar classes tend to be very different.

Bringing consistency to the interconnection aspect of program construction is more than just syntactic sugar. It means that program construction can be brought to a higher level, where a programmer does not have to be concerned with the details of an object but rather with its connections and high-level functions. The details of how a connection is used is unimportant to the programmer, just as the type of signals and the amount of current flowing through the physical PC cables are unimportant to the PC user.

Before going further, I should discuss the basic plug methods. Their number is limited and should not be expanded upon lest the class lose its simplicity and consistency. Table 1 lists the principal methods and operators, and Example 2 shows how some of these are used.

Example 2: How to use the basic methods and operators.

  BOOL t ;
  plugA a, b;

  t = a.connected () ;  // is A connected?
  t = b.usable () ;     // is B usable?

  a <= b ;              // plug B into A
  a -> VariableInB = 1; // set VariableInB to 1

  -  a ;                // disconnect but don't free other end
  -- b ;                // disconnect and free other end

Table 1: The principal methods and operators used with plugs.

  Method            What it does
  -------------------------------------------------------------------------

  A<=B              Connects A to B.  First disconnects them by
                      invoking --A and --B.

  -A                Disconnects A, returns otherEnd.  Does not
                      free otherEnd.

  --A               Disconnects A.  This method does free otherEnd.

  A.connected()     Returns O if A is disconnected.

  A.usable()        Returns O if A is not usable (that is, uninitialized
                     redirector).

  a->V              Accesses member variable V of object at other end of A.
                     Normally assumes something is at the other end.

  R=& A             Changes redirector R's reference to A.  Returns prior
                     reference.

  A.addBefore(B,C)  Connects A to B, and C to whatever A was connected to.
                     If A has a redirector, then it points to C.

  A.addAfter (B,C)  Like addBefore.  If A has a redirector, it does not
                     change.

Table 1 shows the six principal functions used with plugs. Other functions used for internal operations are discussed later. The connected and usable functions return status information about a plug. A plug is always usable. The function is included and will be addressed later in the discussion covering redirectors, an adjunct to plugs. We have already discussed the <= connection method. The element-reference method-> provides access to elements at the other end of a connection, so in a sense, a plug operates like a conventional pointer.

The minus (-) and double-minus (--) operators are used to disconnect a plug. The minus operator indicates that the plug at the other end will be used again, whereas the double-minus operator tells the plug at the other end it will no longer be needed. This is important for dynamically allocated plug objects that must be deleted automatically. Interestingly, it does not matter which end is disconnected first, since the other end is notified and disconnected at the same time. There are no dangling pointers when dealing with plug connections.

Redirectors

Plugs are very useful by themselves. However, the concept of redirector plug classes can further enhance the program construction process. A redirector for a particular plug class looks and acts like a plug, but connections are made to a plug referenced by the redirector. For example, given a and b, where a is of type plugA and b is of type plugB, and given ra where ra is of type plugRedirectorA, the statements ra=&a; b<=ra; will connect plugs a and b together.

This approach is more efficient than connecting b to ra and forwarding all requests to a through ra. Disconnecting ra disconnects a, but the redirection remains in effect. A redirector is essentially turned off by assigning a Null value. The connected function reflects the state of the redirected plug, while the usable function reflects the state of the redirector (that is, it returns False when the redirector is set to Null).

Redirecting a redirector has the expected effect of allowing connections to occur elsewhere, while remaining efficient once a connection is made. Redirectors can be on either side of a connection operator, and there is no limit to the number of redirectors in a chain.

Redirectors are very important when creating components from other components. Typically, the other components have public plugs or redirectors referenced by the outer components' redirectors. Connections to the other components' redirectors efficiently connect to the inner components.

The Source Code

Listing One (page 116) presents PLUGS.H, which defines the macros used to implement plug classes. Listing Two (page 118) contains PLUGS.CPP, which includes both connection definitions and corresponding function definitions. Simple plug operations are also shown. The program's main purpose is to demonstrate the various ways that plugs can be used.

I did not use C++ templates in my implementation, so the code will work with C++ compilers that lack template support (like Microsoft C7). The macros allow plugs to be type-safe so that only one type of plug can be connected to another. Plug class definitions are normally included in header files used to define the base classes used with a plug class.

The statement DECLARE_CONNECTION (plugX, plugXBase, plugY, plugYBase) declares a pair of abstract plug classes, plugX and plugY, and a pair of abstract redirector classes. It assumes that the base classes, plugXBase and plugYBase, are already defined. The base classes contain the functions and variables to be shared when a connection is made.

You need to declare at least two plug classes, one for each end, to make a connection. If you need polymorphic plugs, just declare another plug class. Redirectors are defined in the same manner, but typically only one class will be defined per end. For example, the statement DECLARE_PLUG (aPlugX, plugX) followed by DECLARE_REDIRECTOR (plugXRedirector, plugX) accomplishes this task.

Plug-function definitions are done using the IMPLEMENT_PLUG macro. It defines all but two functions declared for every plug; see Example 3.

Example 3: Defining plugs by invoking the IMPLEMENT_PLUG macro.

  IMPLEMENT_PLUG ( aPlugX, plugX )

  void aPlugX::afterConnect ()

  {
      // operations performed after an initial connection are placed here
  }

  void aPlugX::beforeDisconnect ()
  {
      // operations performed before a connection is broken are placed here
  }

The afterConnect function is called for each plug after a successful connection is made. The order is not specified. These functions allow a plug to initialize the other end accordingly. For example, a plug with internal plug references would make these connections after the main plug is connected. The beforeDisconnect function is also called for each plug, but just before a connection is broken. This is where plugs connected by the afterConnection function would be disconnected. Other plug functions can be redefined by subclassing a plug class.

You can redefine a redirector without additional definitions by invoking the redirector implementation macro, as in: IMPLEMENT_REDIRECTOR (plugXRedirector, plugX). Redirectors need only be defined as needed.

Program Construction

Building a program using plugs assumes that the appropriate components are either available or can easily be built from existing components. You create and connect the various components together in a manner that parallels program data-flow designs. You then initiate operations in the constructed program by invoking appropriate methods. Connections can be dynamically made and broken and pluggable components created and destroyed as necessary.

Program construction using plugs is both powerful and simple. A single connection might interconnect two complex components in a variety of ways. Pluggable objects also need to be viewed at two levels: use and implementation. A programmer using a plug needs to know what kind of plug is at the other end, the general effects of making a connection, and how to use high-level functions provided by the base classes.

The plug implementor must know about connection initialization, termination, and what really happens once a connection is made. You can program at either or both levels. You may even use one set of components while creating another. With plugs, the fewer changes you make to a class, the better. This is in contrast to the subclassing that occurs with class libraries. The latter are used to build new components, while plugs are used to build compound components.

Other Topics

There are many more aspects to the use and implementation of plugs than can be covered in this article (which is why I am writing a book on the subject). Just to give you an idea of what can be done with plugs, consider plugs that allow multiple connections at one time or plugs that can generate new objects for an actual connection and are coordinated in some fashion. The program in Listing Two shows how a plug object can dynamically create new plug objects when a connection is made.

Another aspect of plugs not thoroughly addressed here is their two-sided nature and what can be done with it. In particular, plugs provide the ability to insert new components into an existing structure, as well as the ability to remove a component by disconnecting all the appropriate connections. Why would you want to do this? How about inserting a debugging component within an existing system or replacing a broken component with a working one? With this approach it would be possible for an object library to be supplied, without source, and enhanced by changing the internals. This is normally impractical. The addBefore and addAfter functions are included in the source code examples, but a thorough explanation of them is beyond the scope of this article.


_OBJECT-ORIENTED PROGRAM CONSTRUCTION_
by William Wong


[LISTING ONE]
<a name="0227_000d">

//***** PLUGS.H -- by William G. Wong, Copyright (c) June 1992 *****

#if !defined(_PLUGS_H_)
#define _PLUGS_H_

// ----  Plug Class Definition Macros  ----
// DECLARE_CONNECTION(abstractPlug1,base1,abstractPlug2,base2)
// DECLARE_PLUG(plug,abstractPlug)
// DECLARE_REDIRECTOR(redirector,abstractPlug)
//
// ----  Plug Class Implementation Macros  ----
// IMPLEMENT_PLUG(plug)
//   void plug::afterConnect     () {}
//   void plug::beforeDisconnect () {}
// IMPLEMENT_SIMPLE_PLUG(plug)
// IMPLEMENT_REDIRECTOR(redirector)
//
// ----  plugCheck alternate definition  ----
// Define a check macro if you want to check for invalid access using ->.

#if !defined(plugCheck)
#define plugCheck(ptr)
#endif

// ----  Simple plug macro definitions  ----
#define IMPLEMENT_SIMPLE_PLUG(c1,p1)\
IMPLEMENT_PLUG(c1,p1)\
void c1::afterConnect () {}\
void c1::beforeDisconnect () {}

#if !defined(NULL)
#define NULL ((void *)0)
#endif
#if !defined(FALSE)
#define FALSE 0
#endif
#if !defined(TRUE)
#define TRUE 1
#endif

#define DECLARE_CONNECTION(p1,b1,p2,b2)\
class p1##HiddenRedirector;\
class p2##HiddenRedirector;\
class p2;\
DECLARE_ABSTRACT_PLUG(p1,b1,p1##HiddenRedirector,p2)\
DECLARE_ABSTRACT_PLUG(p2,b2,p2##HiddenRedirector,p1)\
DECLARE_ABSTRACT_REDIRECTOR(p1##HiddenRedirector,p1)\
DECLARE_ABSTRACT_REDIRECTOR(p2##HiddenRedirector,p2)

#define DECLARE_ABSTRACT_PLUG(p1,b1,r1,p2)\
typedef r1 rd##p1 ;\
typedef p2 px##p1 ;\
class p1 : public b1\
{\
 friend class r1;\
 friend class p2;\
 public:\
  virtual ~p1                   () {-- * this;}\
  virtual int  isRedirector     () = 0 ;\
  virtual int  connected        () = 0 ;\
  virtual int  usable           () = 0 ;\
  virtual void operator <=      (px##p1 & newEnd) = 0 ;\
  virtual px##p1 * operator ->  () = 0 ;\
  virtual px##p1 * operator -   () = 0 ;\
  virtual void operator --      () = 0 ;\
  virtual void afterConnect     () = 0 ;\
  virtual void beforeDisconnect () = 0 ;\
  virtual void addBefore        ( px##p1 * inner, p1 * newEnd ) = 0 ;\
  virtual void addAfter         ( px##p1 * inner, p1 * newEnd ) = 0 ;\
  virtual rd##p1 * getRedirector() = 0 ;\
  virtual void setRedirector    ( rd##p1 * newRedirector ) = 0 ;\
  virtual p1 * connectedto      ( px##p1 * newEnd ) = 0 ;\
  virtual void disconnectedFree () = 0 ;\
  virtual void disconnected     () = 0 ;\
};

#define DECLARE_ABSTRACT_REDIRECTOR(r1,p1)\
class r1 : public p1\
{\
 public:\
  virtual p1 * operator = ( p1 * newPlug ) = 0 ;\
  virtual void resetRedirection () = 0 ;\
};

#define DECLARE_PLUG(c1,p1)\
class c1 : public p1\
{\
 public:\
      c1                   () {myPlug=NULL;myRedirector=NULL;}\
  virtual ~c1                   () {-- (* this);}\
  virtual int  isRedirector     () {return TRUE;}\
  virtual int  connected        () ;\
  virtual int  usable           () ;\
  virtual void operator <=      (px##p1 & newEnd) ;\
  virtual px##p1 * operator ->  () ;\
  virtual px##p1 * operator -   () ;\
  virtual void operator --      () ;\
  virtual void addBefore        ( px##p1 * inner, p1 * newEnd ) ;\
  virtual void addAfter         ( px##p1 * inner, p1 * newEnd ) ;\
  virtual rd##p1 * getRedirector () ;\
  virtual void setRedirector    ( rd##p1 * newRedirector ) ;\
  virtual p1 * connectedto      ( px##p1 * newEnd ) ;\
  virtual void disconnectedFree () ;\
  virtual void disconnected     () ;\
  virtual void afterConnect     () ;\
  virtual void beforeDisconnect () ;\
  rd##p1 * myRedirector ;\
  px##p1 * myPlug ;\
};

#define DECLARE_REDIRECTOR(r1,p1)\
class r1 : public p1##HiddenRedirector\
{\
 public:\
  virtual p1 * operator =       ( p1 * newPlug ) ;\
  virtual void resetRedirection () ;\
      r1                   () {myPlug=NULL;myRedirector=NULL;}\
  virtual ~r1                   () {(* this) = NULL;}\
  virtual int  isRedirector     () {return TRUE;}\
  virtual int  connected        () ;\
  virtual int  usable           () ;\
  virtual void operator <=      (px##p1 & newEnd) ;\
  virtual px##p1 * operator ->  () ;\
  virtual px##p1 * operator -   () ;\
  virtual void operator --      () ;\
  virtual void addBefore        ( px##p1 * inner, p1 * newEnd ) ;\
  virtual void addAfter         ( px##p1 * inner, p1 * newEnd ) ;\
  virtual rd##p1 * getRedirector() ;\
  virtual void setRedirector    ( rd##p1 * newRedirector ) ;\
  virtual p1 * connectedto      ( px##p1 * newEnd ) ;\
  virtual void disconnectedFree () ;\
  virtual void disconnected     () ;\
  virtual void afterConnect     () ;\
  virtual void beforeDisconnect () ;\
  rd##p1 * myRedirector ;\
  p1     * myPlug ;\
};

// ----  Implementation definitions  ----
#define IMPLEMENT_PLUG(c1,p1)\
int  c1::connected   () {return myPlug != NULL;}\
int  c1::usable      () {return TRUE;}\
void c1::operator <= (px##p1 & newEnd)\
{\
  if (myPlug != NULL)\
    myPlug -> disconnectedFree () ;\
  if ((& newEnd) != NULL)\
  {\
    myPlug = newEnd.connectedto ( this ) ;\
    if (myPlug)\
    {\
      (*myPlug).afterConnect();\
      (*this).afterConnect() ;\
    }\
  }\
}\
px##p1 * c1::operator -> () {return myPlug;}\
px##p1 * c1::operator -  ()\
{\
  px##p1 * prior = myPlug ;\
  if ( prior != NULL )\
  {\
    beforeDisconnect();\
    prior -> beforeDisconnect();\
    disconnected () ;\
    prior -> disconnected () ;\
  }\
  return prior ;\
}\
void c1::operator -- ()\
{\
  px##p1 * prior = - (* this) ;\
  if (prior) prior -> disconnectedFree () ;\
}\
void c1::addBefore ( px##p1 * inner, p1 * newEnd )\
{\
  rd##p1 * oldRedirector = myRedirector ;\
  addAfter ( inner, newEnd ) ;\
  if (oldRedirector!=NULL)\
  {\
    oldRedirector -> resetRedirection () ;\
    (* oldRedirector) = newEnd ;\
  }\
}\
void c1::addAfter ( px##p1 * inner, p1 * newEnd )\
{\
  if (myPlug && newEnd)\
    (* newEnd) <= (* (- (* this))) ;\
  (* this) <= (* inner) ;\
}\
rd##p1 * c1::getRedirector () {return myRedirector;}\
void c1::setRedirector ( rd##p1*newRedirector ) {myRedirector=newRedirector;}\
p1 * c1::connectedto ( px##p1 * newEnd )\
{\
  if (myPlug)\
    -- (* myPlug) ;\
  myPlug = newEnd ;\
  return this ;\
}\
void c1::disconnectedFree () {myPlug=NULL;}\
void c1::disconnected     () {myPlug=NULL;}

// ----  Define for redirector methods  -----
#define IMPLEMENT_REDIRECTOR(r1,p1)\
p1 * r1::operator = ( p1 * newPlug )\
{\
  p1 * prior = myPlug ;\
  resetRedirection () ;\
  if (newPlug != NULL)\
  {\
    myPlug = newPlug ;\
    newPlug -> setRedirector(this) ;\
  }\
  return prior ;\
}\
void r1::resetRedirection () {myPlug=NULL;}\
int r1::connected ()\
 { return (myPlug == NULL) ? FALSE : myPlug -> connected () ; }\
int  r1::usable ()\
 { return (myPlug == NULL) ? FALSE : myPlug -> usable () ; }\
void r1::operator <= (px##p1 & newEnd)\
 {if (myPlug) (* myPlug) <= newEnd ;}\
px##p1 * r1::operator -> ()\
 {return (myPlug) ? myPlug -> operator -> () : NULL ;}\
px##p1 * r1::operator - ()\
 {return (myPlug) ? - * myPlug : NULL ;}\
void r1::operator -- ()\
 {if (myPlug) -- * myPlug ;}\
void r1::addBefore ( px##p1 * inner, p1 * newEnd )\
{\
  if (myPlug)\
  {\
    myPlug -> setRedirector (NULL) ;\
    (* myPlug).addBefore ( inner, newEnd ) ;\
  }\
  if (newEnd)\
    newEnd -> setRedirector ( this ) ;\
   myPlug = newEnd ;\
}\
void r1::addAfter ( px##p1 * inner, p1 * newEnd )\
{\
  if (myPlug)\
    (* myPlug).addAfter ( inner, newEnd ) ;\
}\
rd##p1 * r1::getRedirector () {return myRedirector;}\
void r1::setRedirector ( rd##p1 * newRedirector )\
 {myRedirector=newRedirector;}\
p1 * r1::connectedto ( px##p1 * newEnd )\
 {return (myPlug) ? (* myPlug).connectedto(newEnd) : NULL ;}\
void r1::disconnectedFree ()\
 {if (myPlug) myPlug -> disconnectedFree();}\
void r1::disconnected     ()\
 {if (myPlug) myPlug -> disconnected();}\
void r1::afterConnect     ()\
 {if (myPlug) myPlug -> afterConnect();}\
void r1::beforeDisconnect ()\
 {if (myPlug) myPlug -> beforeDisconnect();}

#endif





<a name="0227_000e">
<a name="0227_000f">
[LISTING TWO]
<a name="0227_000f">

// *** PLUGS.CPP--Example of Use of Plugs by WIlliam G. Wong Copyright 1992 ***
// ****************************************************************

#include "plugs.h"

// ====  Class definitions  ====
//
// These definitions are normally found in a header file.
//
// ----  Simple abstract plug that can link to objects link itself  ----

class plugXBase      // simple accessible class
{
  public :
    int plugXBaseCommonItem ;
} ;

class plugYBase {} ;   // nothing provided here
         // plug used to access plugXBase only

// ----  Declare plugs  ----

DECLARE_CONNECTION(plugX,plugXBase,plugY,plugYBase)
DECLARE_PLUG(aPlugX,plugX)
DECLARE_PLUG(aPlugY,plugY)
DECLARE_REDIRECTOR(plugXRedirector,plugX)


// ----  Dynamic allocation example  ----
//
// This pair of plug classes work like plugX except that a single
// plug creates a new object each time a connection is made. The
// plug object is freed when the connection is broken.
//
// Both <= and connectedto must be redefined because a connection
// will come through one or the other depending upon which side the
// multiPlugX object is when the initial connection is initiated.

class dynamicPlugX : public aPlugX
{
  public:
    void disconnectedFree () { delete this ; } ;
} ;

class multiPlugX : public aPlugX
{
  public:
    void operator <= ( plugY & newEnd )
      { newEnd <= * new dynamicPlugX ; }
    plugX * connectedto ( plugY * newEnd )
      { return (* new dynamicPlugX).connectedto ( newEnd ) ; }
};

// ----  sample1 to sample2 connection  ----
class sample1Base
{
  public:
    aPlugX x, y ;
} ;

class sample2Base {} ;

// ----  Declare plugs  ----

DECLARE_CONNECTION(Sample1,sample1Base,Sample2,sample2Base)
DECLARE_PLUG(sample1,Sample1)
DECLARE_PLUG(sample2,Sample2)

// ----  A slightly more complex plug base  ----
class myPlugBase
{
  public:
    int i ;
    virtual void read  ( int x ) { i = x ; } ;
    virtual void write ( int x ) { i = x ; } ;

    myPlugBase () { i = 1 ; } ;
} ;


// ----  Declare plugs  ----
DECLARE_CONNECTION(MyPlug1,myPlugBase,MyPlug2,myPlugBase)
DECLARE_PLUG(myPlug1,MyPlug1)
DECLARE_PLUG(myPlug2,MyPlug2)


// ====  Implementation Definitions  ====

IMPLEMENT_PLUG(aPlugX,plugX)
IMPLEMENT_PLUG(aPlugY,plugY)

void aPlugX::afterConnect ()
{
  // initial connection code goes here
}

void aPlugX::beforeDisconnect ()
{
  // disconnect code goes here
}

void aPlugY::afterConnect ()
{
  // initial connection code goes here
}

void aPlugY::beforeDisconnect ()
{
  // disconnect code goes here
}

IMPLEMENT_REDIRECTOR(plugXRedirector,plugX)


// ----  Implementation for simple plugs  ----
//
// Generates null afterConnect and beforeDisconnect functions.

IMPLEMENT_SIMPLE_PLUG(sample1,Sample1)
IMPLEMENT_SIMPLE_PLUG(sample2,Sample2)


// ----  Implementat plugs  ----
//
// myPlug1a and myPlug1b redifine methods from the base class.

IMPLEMENT_SIMPLE_PLUG(myPlug1,MyPlug1)
IMPLEMENT_SIMPLE_PLUG(myPlug2,MyPlug2)

class myPlug1a : public myPlug1
{
  public :
    int i1 ;
    virtual void write  ( int x ) { i1 = x ; } ;
} ;


class myPlug2a : public myPlug2
{
  public :
    int i2 ;
    virtual void write  ( int x ) { i2 = x ; } ;
} ;


// ====  Sample program  ====

void test ( myPlug1 & pp1 )
{
  aPlugX     x1, x2, x3 ;
  plugX *    ppx ;
  plugXRedirector rx1, rx2 ;
  aPlugY     y1, y2, y3 ;
  sample1    s1 ;
  sample2    s2 ;
  myPlug1a   p1 ;
  myPlug2a   p2 ;
  multiPlugX mpx ;

  // ----  General connections  ----
  x1 <= y1 ;         // connect x1 and y1
  y1 <= x2 ;         // disconnect x1
            // connect    y1 and x2
  s1 <= s2 ;         // connect types must match

  // ----  Compound linkages  ----
  s2 -> x <= y1 ;      // linking s1.x to y1
  s2 -> y <= y2 ;      // linking s2.y to y2


  // ----  Redirector examples  ----
  rx1 = & x1 ;         // setup redirector
  rx1 <= y1 ;         // connect x1 to y1 through r1
  y1 -> plugXBaseCommonItem = 1 ;
            // access item at other end
  rx1.addBefore ( & y2, & x3 ) ;// rx1 redirects x3, x1 connected to y2
            //  and x3 is attached to y1
  rx2 = & x3 ;         // setup a different redirector type
  rx2 <= y2 ;         // connects y2 to x3 through r2
  rx2 = NULL ;         // reset redirector

  // ----  Multiplug examples  ----
  // This generates two new objects and deletes them when they are
  // no longer of use. Note how pp is used to keep around a reference
  // to (mp<=a) (the one created for a) which is later used to link to c.

  mpx <= y1 ;         // y1 connected to new object
  mpx <= y2 ;         // y2 connected to different object

  ppx = - y1 ;         // disconnect but keep around object

  (* ppx ) <= y3 ;      // link object to y3

  -- x2 ;               // free up the other end of x2

  ppx = - y3 ;         // - disconnects and returns pointer
  -- * ppx ;         // -- * frees result


  // ----  Plugs with different variables or methods  ----
  p1  <= p2 ;

  p1 -> read ( p1 -> i ) ;   // using read and i from p2

  pp1 <= p2 ;         // connects pp1 to p2
            // disconnects p1

  // ---- Explicit disconnects  ----
  -- s2 ;         // forced disconnect
  -- s1 ;

  if (x1.connected())      // determine if connection exists
    -- x1 ;
  // ---- Implicit disconnects for locally defined objects  ----
}
// ------------main()--------
void main ()
{
  myPlug1a p1 ;

  test ( p1 ) ;
}
================================================================















Copyright © 1992, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.