Appendix B Creating Wrapped Classes

B.2 Inheritance Model of the Wrap Script

The wrap script implements inheritance in a manner analogous to the way it is done by hand -- it essentially copies-and-pastes inherited descriptions into derived wrap descriptions. In this section we explain the copying and pasting, the restrictions on creating a subclass, and the hooks that we provide to allow wrap descriptions to work when pasted into subclasses.

Frequently, the foundation classes that we want to wrap have an inheritance relationship. Let's suppose that we want to wrap a class called PrintableIntStack. This new class is a subclass of the IntStack class that we discuss in the example in Section 1.2.2, and it adds the method
getPrintString to its interface, which returns a string that describes the stack.

Using only the features described in the previous section, we would have to create a new wrap file
that includes all of the wrap description of the existing DSintStack wrapped class referred to in Section 1.1, but which has PrintableIntStack as its foundation class and getPrintString as a published method. As you might expect, this leads to maintenance nightmares, especially since wrapped classes often have more methods and members than DSintStack does.

Let's look at this example two ways: first without inheritance and then with inheritance.

B.2.1 Example without Inheritance

notice
=====================================================================
/*
 *  $Header: /mit/ceci/1/aybee/devl/doc/firstRel/RCS/
DSprintableIntStackROsansInheritance.wrp,v 1.1 1995/06/02 21:23:23 aybee Exp aybee $
 *
 *	Copyright 1993, 1994, 1995 Massachusetts Institute of Technology.
 *	All rights reserved.
 *	AthenaMuse is a registered trademark of the Massachusetts
 *	Institute of Technology.
 */
=====================================================================
foundation: IntStack
wrapped:    DSprintableIntStack
module:     ExampleModule
abstract:   false

header
====================================================================
#include <PrintableIntStack.h>
/*
 * PrintableIntStack.h hypothetically includes something like...
 *
 * #include <PrintableIntStack.h>
 *
 *   class PrintableIntStack: public IntStack
 *   {
 *     public:
 *       char* getPrintString();
 *    };   
 *
 */
======================================================================

source
======================================================================
#include <control/EXmodule.h>
#include <control/DSprintableIntStackRO.h>
#include <adl/ERsemantic.h>
======================================================================

codeFragments
{
constructor
======================================================================
mpWrapped = new PrintableIntStack;
======================================================================
}



members
{
private:


published:
  name
  {
    get: default
    set: default
  }
  height
  {
    get: custom
    set: none
  }
}

methods
{
private:
  void localDestroy()
  ======================================================================
  if (mpWrapped)
     delete mpWrapped;
  ======================================================================

  UTvalue _Get_height() const
  ======================================================================
  return mpWrapped->getHeight();
  ======================================================================
	
published:
  void Push( integer_t newTop )
  ======================================================================
  /* effects: puts newTop on the top of the stack */
  mpWrapped->push( newTop );
  ======================================================================
  
  integer_t Pop()
  ======================================================================
  /* returns: removes top value from the stack and returns it */
  assert( ! mpWrapped->isEmpty() );
  return mpWrapped->pop();
  ======================================================================

  UTstring GetPrintString( )
  ======================================================================
  /* effects: puts newTop on the top of the stack */
  return mpWrapped->GetPrintString();
  ======================================================================
}

B.2.2 Example with Inheritance

notice
======================================================================
/*
 *
 *  $Header: /mit/ceci/1/aybee/devl/doc/firstRel/RCS/DSprintableIntStackRO.wrp,v 1.1 1995/06/02 21:23:23 aybee Exp aybee $
 *
 *	Copyright 1993, 1994, 1995 Massachusetts Institute of Technology.
 *	All rights reserved.
 *	AthenaMuse is a registered trademark of the Massachusetts
 *	Institute of Technology.
 */
======================================================================

foundation: PrintableIntStack
wrapped:    DSprintableIntStack
module:     ExampleModule
abstract:   false

header
======================================================================
#include <PrintableIntStack.h>
/*
 * PrintableIntStack.h hypothetically includes something like...
 *
 * #include <PrintableIntStack.h>
 *
 *   class PrintableIntStack: public IntStack
 *   {
 *     public:
 *       char* getPrintString();
 *    };   
 *
 */
======================================================================

source
======================================================================
#include <control/EXmodule.h>
#include <control/DSprintableIntStackRO.h>
#include <adl/ERsemantic.h>
======================================================================

superclasses
{
  DSintStack
}

codeFragments
{
constructor
======================================================================
mpWrapped = new PrintableIntStack;
======================================================================
}

methods
{
published:
  UTstring GetPrintString( )
  ======================================================================
  /* effects: puts newTop on the top of the stack */
  return mpWrapped->GetPrintString();
  ======================================================================
}

B.2.3 An Explanation of the Wrap Inheritance Description

For a discussion of the notice, wrapped, module, and abstract fields, see Section B.2.3, "An Explanation of the Parts of a Wrap File" page 274.

Superclasses

This section of the description lists the wrap descriptions that are superclasses for this class. See Section B.2.2, "Example with Inheritance" page 282 for the format for specifying superclasses. Each line between the brackets should contain the name of a superclass description.

The wrap script uses each superclass name listed to find the corresponding description. It looks for a file named "superclassNameRO.wrp" somewhere in the wrap path. In our example, the wrapper looks for DSintStackRO.wrp. The wrapper ensures that the ADL run-time engine thinks of each listed class as a superclass of the given class.

Foundation: and FoundationRO:

To allow descriptions to inherit the code from their superclass descriptions, it is necessary that all code that manipulates the mpWrapped member work with the mpWrapped member of the subclass as well. For this reason, a wrap description can inherit only from a wrap description whose foundation class is a void or a C++ superclass of its foundation class. This provides the required C++ type safety since the wrap script is not intelligent enough to check this relationship at wrap-time. It generates code that attempts to cast from a NULL pointer to the subclass's WRAP_FOUNDATION to a pointer to the superclasses's WRAP_FOUNDATION. If the foundation classes do not have the proper relationship, this code intentionally causes a compile-time error; if all goes well, the compiler lists the error as occurring on the line containing the foundation declaration.

For example, DSprintableIntStack is a subclass of DSintStack. Their foundation classes are
PrintableIntStack and IntStack, respectively, so PrintableStack must be a subclass of IntStack. The code that the wrap script generates to check the pointer types is as follows:

(IntStack*) tempPtr = (PrintableIntStack*) NULL;

Header and Source Sections

Subclasses inherit the contents of the header and source sections of their superclasses by concatenation. These sections appear in an order that ensures that a class' header section does not appear until after the header sections of its superclasses.[21] We refer to this as the proper order.

Members

A subclass inherits members. The wrap script places the contents of the member section of a superclass of a wrapped class in the subclass in the proper order. Subclasses cannot have members with the same name as their superclasses' members. The wrap script detects duplicate published member names, but since the wrapper does not interpret the contents of the C++ member sections, duplicates in those sections are not be caught until compile-time. Note that members are placed at the same protection level as in the superclass and are not moved to another level as in C++ subclasses. That is, inherited private members are listed as private members of the subclass and are accessible to that class.

Methods

A subclasses inherits methods, and it is possible to override inherited methods. Providing a method with the same name as a method provided by a superclass overrides that method. Just as in the ADL, methods are inherited in a depth-first fashion. Unlike the ADL, local methods (such as ADL constructors) are inherited. This is to encourage wrap class writers to provide a consistent ADL constructor interface.[22]

The contents of constructor and constructorFromFoundation are not inherited. This is in keeping with the C++ treatment of constructors, and it is partly attributable to the special meaning of the mpWrapped member. No matter how many wrapper descriptions a class inherits from, it has only one mpWrapped member. The wrap script provides the preConstructor and wrappedIsReady fragments (which are inherited by concatenation in the proper order) to allow a superclass to initialize its members before and after mpWrapped is constructed and ready for operation.

Useful Details

In addition to defining WRAP_FOUNDATION and WRAP_FOUNDATION_PTR, as described above, the wrapper provides WRAP_IS_ABSTRACT and WRAP_IS_ADLSUBCLASSABLE, which are either 0 (false) or 1 (true) depending on whether or not the wrapped class is abstract and adlSubclassable respectively.

In an effort to cut down on the size of wrapped classes, the wrapper does not completely fill out the class description for abstract, non-adlSubclassable classes since no instances of them will ever be created. We call those ANAS classes. In addition, it does not include all of the inherited header and source declarations. Unfortunately, hidden in the source sections are the includes of the generated header file and the include of the module in which the class is generated. Instead of including the whole source section to provide the correct include statements, the wrapper uses some heuristics to find and include only the needed include directives.

It is possible that the wrap script's attempted short cuts will be too effective and break something. The optional wrap directive ANAScuts: boolean keeps the wrapper from taking short cuts in any class for which it is set to true, as well as in all of its subclasses. This feature is provided only as an escape hatch in the unlikely event that the heuristics fail. If you have to use this escape hatch, please let us know.


[21] The ActivityManager class is an exception. To work around a bug in the HP compiler having to do with templates and the order in which they and their declarations are seen, the source section of the ActivityManager class is always placed last in its subclasses.
[22] It also a big win in large wrapped hierarchies, such as the XFwidgets, where we do not have to respecify the ADL constructor over and over again.
B.2.1 - Example without Inheritance
B.2.2 - Example with Inheritance
B.2.3 - An Explanation of the Wrap Inheritance Description
Superclasses
Foundation: and FoundationRO:
Header and Source Sections
Members
Methods
Useful Details

AM2 Documentation - 19 NOV 1996

Generated with Harlequin WebMaker