[Date Prev][Date Next][Thread Prev][Thread Next][Author Index][Date Index][Thread Index]

CONSTRUCT_ON... and NOT_A_TYPE documentation.



Synopsis:

 - Documentation for CONSTRUCT_ON() and RETURN_CONSTRUCT_ON() macros.
 - Documentation for NOT_A_TYPE X++ feature.

======================================================

I have added two features to X++.  These had been discussed locally, but
today it was brought to my attention that I had not documented them.
Therefore, documentation follows.

The CONSTRUCT_ON() and RETURN_CONSTRUCT_ON() will be in the next merge.

The NOT_A_TYPE feature should be there, too, unless eric gets another
merge out before I get the latest files to him.

======================================================

	CONSTRUCT_ON(HEAP,VAR,TYPE,ARGS)
	RETURN_CONSTRUCT_ON(HEAP,TYPE,ARGS)

These are exactly analogous to the macros

	CONSTRUCT(VAR,TYPE,ARGS)
	RETURN_CONSTRUCT(TYPE,ARGS)

except that they allow you to specify the heap on which the new object
is to be constructed, rather than requiring you to set up the fluid
variable "allocType".  They protect the construction with a constructor
bomb, create the new heaper with an appropriate "operator new", and
so on. 

Where a C++ programmer would say:

	ptrVar = new Foo(arg1,arg2,...);

an X++ programmer would say:

	CONSTRUCT_ON(ptrVar,HEAP,Foo,(arg1,arg2,...));



Where a C++ programmer would say:

	return new Foo(arg1,arg2,...);

an X++ programmer would say:

	RETURN_CONSTRUCT_ON(HEAP,Foo,(arg1,arg2,...));


HEAP may currently be one of:

 - PERSISTENT
 - OTHER
 - YOUR_CHOICE

======================================================

	The NOT_A_TYPE mechanism.

In the X++ programming paradigm, the client interface to a class
is normally defined in an abstract superclass (base class), and
one or more concrete classes, derived from that abstract class
and called "implementation subclasses", define most of the actual
behavior.

The client normally holds instances of such classes by a strong pointer,
wimpy pointer, or checked pointer, whose type is (effectively)
"AbstractClass *".  The client normally doesn't know (except perhaps
when it creates an instance), or care, which concrete subclass it is
holding.  It may have an actual object, or a proxy for one that is
on another machine or in another address space.  It may hold a number
which has different types if it is zero, negative, or very large.
Indeed, it may hold an object that changes its actual type from moment
to moment, as a side-effect of its operation, or of the operation of
other objects.

It is common for some, but not all, of the implementation subclasses
of an abstract class, to define some extra protocol.  If a client
holds the class by a pointer whose type is (effectively)
(ConcreteSubclass *), or perhaps (IntermediateAbstractSubclass *),
the client could, without compiler-detected error, attempt to use
this extra protocol.  If the object it actually held was, or had
become, an instance of some type which did not implement the protocol,
great havoc would ensue.

The NOT_A_TYPE mechanism exists to prevent this.  Typically, the abstract
class will be defined normally, and its subclasses will be declared
NOT_A_TYPE.  When a class is so declared, it cannot be held by a strong,
wimpy, or checked pointer, because the necessary classes to support the
pointers are not defined.  (Attempts to so hold it will cause the compiler
to complain that the classes "SP2_Type", "WP2_Type", or "CP2_Type" are
undefined.  Note that these errors also appear when it is legitamate to
hold a pointer to that class, but an include file defining the class is
missing.)

When a client actually has reason to interact directly with implementation
classes, several mechanisms allow this.

First, when the client may use the CAST() macro, to cast the pointer down
to a likely actual type.  If the actual object is not the type expected,
CAST() may (for some classes) coerce the class to change to the desired
type, or (more typically) will BLAST() (which can be caught on a SHIELD()).
(Modules with sufficient understanding of the implementation's guts can
switch off CAST()'s error detection and type coercion, making its behavior
that of a normal cast.  This should be avoided.)

Second, CHOOSEn() or COMMY_CHOOSEn() macros may be used to execute
appropriate code fragments, depending on the actual type (or intermediate
abstract type) of the object.  At execution time the code generated by
these macros determines the actual type of the instance, then CAST()s
the pointer appropriately, stores the result in an "AppropriateType *"
bare pointer, and executes the appropriate code fragment.

Finally (and to be avoided whenever possible), there is nothing to prevent
the client from holding a bare "Type *" pointer, or (unsafely) casting a
pointer down to such a type.  Xlint will complain, of course, but this
complaint can be suppressed or ignored.

Now that I've told you all about WHY to do it, here's HOW to do it:

Where you would use "CHECKED_CLASS(DerivedType)", substitute
"CHECKED_CLASS_NOT_A_TYPE(DerivedType):

Where you would use "DEFINE_CHECKED_CLASS(DerivedType,BaseType)",
substitute "DEFINE_CHECKED_CLASS_NOT_A_TYPE(DerivedType,BaseType)".

Where you would use "DEFINE_DEFERRED_CHECKED_CLASS(DerivedType,BaseType)",
substitute "DEFINE_DEFERRED_CHECKED_CLASS_NOT_A_TYPE(DerivedType,BaseType)".

(Classes which are not CHECKED are always held with bare "Type *" pointers,
so the mechanism does not work with them.  Perhaps we should change to
using wimpy pointers everywhere, instead, and supplement "CLASS(DerivedType)"
with "CLASS_NOT_A_TYPE(DerivedType)", and "DEFINE_CLASS(DerivedType,BaseType)"
with "DEFINE_CLASS_NOT_A_TYPE(DerivedType,BaseType)".)

======================================================

	michael