Appendix B Creating Wrapped Classes

B.2 How to Wrap a C++ Class

B.2.1 Lexical Conventions

The wrap script ignores white space in a wrap file. However, some parts of the description must be on lines by themselves, such as delimiters above and below sections containing C++ code not meant for interpretation by the wrap script. The delimiter is a line containing at least 3 consecutive equal signs (=). We recommend using long sequences to provide a good visual break. Both multi-line ( /*... */ ) and single line ( // ) comments are allowed in these blocks, since the compiler does not interpret the code. For example:


==========================
// Uninterpreted C++ code belongs in here.
/* The bar above and below it must be on lines by themselves. */   
==========================

Outside of these uninterpreted blocks use single line (//) comments to annotate the wrap file, anywhere but the delimiter (====) lines.

B.2.2 The Wrap File: an Example

In the following example, we use the wrapped class DSIntStackRO to look at each part of a wrap file. We explain the components of this file in Section B.2.3, "An Explanation of the Parts of a Wrap File" page 274.

notice
======================================================================
/*
 *
 *  $Header: /mit/ceci/1/aybee/devl/doc/firstRel/RCS/DSintStackRO.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:    DSintStack
module:     ExampleModule
abstract:   false

header
======================================================================
#include <IntStack.h>
/*
 * IntStack.h hypothetically includes something like...
 *
 *   class IntStack
 *   {
 *     public:
 *       int  getHeight();
 *       void push(int);
 *       int  pop ();
 *       int  isEmpty();
 *    };   
 *
 */
======================================================================

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

superclasses
{
}

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

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();
  ======================================================================
}

B.2.3 An Explanation of the Parts of a Wrap File

Notice

This section lets you protect your intellectual property by housing things such as the copyright notice and the RCS $Header$ string.[19] The wrap script places a copy of this section at the top of both the generated header file and the generated source code file. We usually place the contents of the file named "standard_header" in this section.

Foundation or FoundationRO Declaration

The wrap script encourages programmers to implement a wrapped class by using an instance of a foundation class. As encouragement, the wrapper provides a member named mpWrapped in the generated class. You can use this member to point to an instance of the wrapped class. The member mpWrapped can be either a C++ pointer or a reference counted pointer.

This is a required field and you must use one of these declarations to tell the wrap script the type of the foundation class:

foundation: className
This line must contain the designation foundation: followed by the name of foundation class. The mpWrapped member is of type className. In the few cases where there is no underlying foundation class to support the wrapped class, you can specify void as the class name.

foundationRO: classNameRO
This line must contain the designation foundationRO: followed by the name of foundation class that includes the RO suffix. The mpWrapped member is of type classNameRO.

For your convenience the wrap script provides two macros WRAP_FOUNDATION and WRAP_FOUNDATION_PTR, defined as the name of the foundation class and the type of a pointer to that class respectively. These become especially useful when you want to take advantage of wrapper inheritance, as discussed in Section B.2, "Inheritance Model of the Wrap Script" page 280.

The Wrapped Class Name

wrapped: nameOfWrappedClassInADL
This is a required field and must contain the designation wrapped: followed by the name of the wrapped class in the ADL. The latter must be a legal ADL identifier. (See Section 3.2, "Identifiers" page 14.)

Module Declaration

module: moduleName
This is a required field and must contain the module name followed by a legal ADL identifier. See Section B.2, "Inheritance Model of the Wrap Script" page 280 for an explanation about the module mechanism that AM2 uses. You must add a line such as UT_FORCE_LOAD(nameOfWrappedClassInADL) to the appropriate module source file to force the linker to pull in the newly wrapped class.

Abstract Declaration

abstract: boolean

This is a required field. The boolean must be either TRUE or FALSE. If true, this class may not be instantiated in the ADL. This becomes useful when you use wrapped class inheritance and have base classes that should not be instantiated.

Can You Create a Subclass in the ADL?

adlSubclassable: boolean

This is an optional field. The boolean must be either true or false. If true, ADL programmers may use this class as a direct base of any ADL class. Usually, only abstract classes may not have a subclass in the ADL. As a result, the default value for this characteristic is the logical not of the previous declaration abstract. If a class is abstract, then the default is that you cannot create a subclass in the ADL; if a class is not abstract, then the default is that you can create a subclass in the ADL.

The Header Section

At compile-time, wrap script inserts the header section into the generated header file before declaring the class. Use this section to include declarations for required types, most notably the declaration of the foundation class.

The Source Section

At compile-time, the wrap script inserts the source section into the generated source file before defining the methods of the generated class. Use this file to include header files for classes used in the implementation. The wrap script checks that there is an include statement that appears to include the generated header file, and exits with an error message if it does not find this include statement. For instance, in our stack example it checks that there is an include statement that looks something like #include <...DSintStackRO.h>, where the ellipsis represents any (or no) directory specification.

Code Fragments

You may need to insert code sections in various places, as described in this section. To do so, create a section called codeFragments as shown in the example in Section B.2.2, "The Wrap File: an Example" page 272. Legal names for these sections include:

constructor and preConstructor

The wrap script inserts code in the constructor fragment into the C++ constructor for the wrapped object. The code looks like the following:

wrappedClassNameRO(const MCprogramObjectRP& pParent,
		const MCinstanceRP& pDerived= gkpNullInstPtr)
		mpWrapped( 0 )
 {
	// ...contents of "preConstructor" fragment here...
	// ...contents of "constructor" fragment here...
 }

constructorFromFoundation

The wrap script provides a constructor for the wrapped class when a constructorFromFoundation fragment is present. The code looks like the following:

wrappedClassNameRO(WRAP_FOUNDATION_PTR pWrapped,
		const MCprogramObjectRP& pParent,
		const MCinstanceRP& pDerived=gkpNullInstPtr)
 {
	mpWrapped = pWrapped; 
	// ...contents of preConstructor fragment here...
	// ...contents of constructorFromFoundation fragment here...
 }

wrappedIsReady

The contents of wrappedIsReady goes in a fragment similar to:

void wrappedIsReady() { /* fragment code here */ }

A wrapped object should call the wrappedIsReady() function when the foundation object it wraps is ready. This is very useful in the case of wrapper inheritance, further discussed in Section B.2, "Inheritance Model of the Wrap Script" page 280.

preDestroy and postDestroy

If code fragments for preDestroy and postDestroy exist, their contents go in methods similar to:

void preDestroy()     { /* fragment code here */ }  
void postDestroy()    { /* fragment code here */ }

The wrapped object's Destroy method then has the following body:[20]

{ 
preDestroy();            //only inserted if preDestroy exists
localDestroy();           //only inserted if localDestroy exists
mpWrapped = NULL; 	
postDestroy();           //only inserted if postDestroy exists
MCcxxObjectRO::Destroy();//normal upchained Destroy call 
}

Members

Use the members section to introduce members in the generated class. See Section B.2.2, "The Wrap File: an Example" page 272 for the syntax for introducing members.

The members of a wrapped class have four protection levels: private, protected, and public in C++, and published in the wrapper. The wrap script prepares members in the published category, making them available to the ADL run-time engine. You can separate member descriptions into protection levels by placing the protection level name followed by a colon, such as

private:

on a separate line. All subsequent lines until the next protection level designation or the final delimiter ( } ) are given that protection level. The wrap script does not interpret lines with a C++ protection level of private, protected, or public. They go directly into a similarly protected section of the generated header.

Published members appear to the ADL as instance members of the published class. There are two generic operations performed on simple members: get and set. Use the get operation when an ADL program needs the value of a member, and the set operation when the program assigns a value to the member. The wrap script allows you to specify how to handle get and set operations on each published member: by using the default accessor, by using a custom accessor, or by preventing access.

Published members have a special format, as shown in the following example:

				memberName 
				{
					get: accessStyle
					set: accessStyle
				}
Put each statement on a line by itself and designate the access style to be one of the following: default, custom, or none.

If a member's access style is default, the wrap script automatically provides the generated class with a member of type MClvalue for storing that value. The member's name is its published name preceded by an m. In our example, the wrap script would provide the wrapped class with a member named mname.

Sometimes a member doesn't really exist as a member of the wrapped class. For instance, in our example, the foundation IntStack class already maintains the height, so it would be wasteful for us to maintain that information in the wrapped class too. For cases such as this, the wrap script allows you to specify a custom access method for a member. If you specify a custom get statement for a member, you must provide a method with the following signature, where memberName represents the appropriate member's name:

          UTvalue _Get_memberName() const
In our example, we provide a method called _Get_height that asks its foundation object for the current height. If you specify a custom set method, you must provide a method with the following signature:

void _Set_memberName( const UTvalue& newVal )

If it made sense to set the height of a stack, you could specify that the set accessor of height be custom and provide a method named _Set_height.

You can tell the wrap script to specify an access style of none to handle the case where you do not want to allow get and set operations on members. In our example, height has a set access method of none, causing a fatal error for in any ADL program that attempts to set it.

What methods should the wrapped class have?

Use the methods section to describe the methods, or operations, for the wrapped class. See Section B.2.2, "The Wrap File: an Example" page 272 for the syntax for specifying methods. The methods of a wrapped class have the same four protection levels as members: private, protected, and public in C++, and published in the wrapper. The wrap script prepares methods in the published category, making them available to the ADL run-time engine.

Each of the protection levels is optional and contains method descriptions. A method description consists of the method's C++ signature, followed by a block of uninterpreted C++ code (set off by === delimiters). The wrap script extracts the signatures and places them in the proper protection level section of the generated header, and places the definition in the generated source file. If you specify the keyword inline, the wrap script places the body of the method in the header too. Specify the keyword ctor to mark an ADL constructor. Marking a method with ctor is equivalent to using the keyword upon in the ADL. The keyword local marks a method as it does in the ADL. ADL subclasses of the wrapped class do not inherit the method, but wrapped subclasses do.

The wrap script exports methods listed in the published section to the ADL as methods with a corresponding name and ADL signature. The following types are the only legal arguments and return types for published methods:

Arguments and Return Types for Published Methods
C++ typeADL type
Ebooleanboolean
integer_t integer
UThandlehandle
UTintervalinterval
UTlistlist
real_t real
UTstringstring
UTtimetime
UTvalueany
UvalueTypevtype

As a special case, you can also have the return type void. It is equivalent to not declaring a return type in the ADL.


[19] RCS is a source code control system widely used by programmers.
[20] The Destroy method is part of the Reference Counting mechanism and is beyond the scope of this chapter.
B.2.1 - Lexical Conventions
B.2.2 - The Wrap File: an Example
B.2.3 - An Explanation of the Parts of a Wrap File
Notice
Foundation or FoundationRO Declaration
The Wrapped Class Name
Module Declaration
Abstract Declaration
Can You Create a Subclass in the ADL?
The Header Section
The Source Section
Code Fragments
constructor and preConstructor
constructorFromFoundation
wrappedIsReady
preDestroy and postDestroy
Members
What methods should the wrapped class have?

AM2 Documentation - 19 NOV 1996

Generated with Harlequin WebMaker