MAINSAIL Language Manual, Chapter 9

previous   next   top   complete contents   complete index   framed top   this page unframed


9. CLASSes and POINTERs

A CLASS is a compiletime construct that describes records (Chapter 10), data sections (Chapter 11), or storage templates for accessing regions of static memory (Section 9.2.2).

CLASSes themselves do not contain data manipulated by user programs, but instances of a CLASS (that is, the records and data sections described by the CLASS) constitute much of the data of a typical program. Instances of a CLASS may be dynamic (accessed through POINTERs), as in the case of dynamic records and data sections, or inplace (accessed without POINTERs), as in the case of inplace records.

An object known as a CLASS descriptor (see Section 33.39) acts as a runtime proxy for a CLASS. A CLASS descriptor is useful in certain operations that, at runtime, manipulate objects of a CLASS that was not known at compiletime. In many programs, compiletime CLASS declarations suffice to describe the programs' data structures completely, and so such programs have no need to use CLASS descriptors explicitly.

CLASSes are created by compiletime declarations, and CLASS descriptors are allocated automatically at runtime, as needed, based on the CLASSes in the program. It is also possible to create a CLASS descriptor at runtime, using the system PROCEDURE $createClassDscr (see Section 32.68); however, you should rarely need to do this.

9.1. CLASS Declarations

The most common form of a CLASS declaration is:

CLASS v (declarations of fields of v)

where v is an identifier for the name of the CLASS, and the field declarations are separated with semicolons. The field declarations may be for data or PROCEDURE fields. Data field declarations have the form of simple or ARRAY variable declarations or empty declarations, as described in Chapter 6; PROCEDURE field declarations are PROCEDURE headers, as described in Chapter 7 and Section 11.4.

A CLASS containing PROCEDURE fields is useful only for describing data sections; it cannot describe records or storage templates for static memory. Note that a CLASS never provides an implementation for one of its PROCEDURE fields; a PROCEDURE implementation must be provided by a MODULE, not a CLASS (see Chapter 11).

The CLASS declaration:

CLASS c (STRING strINTEGER base,val)

declares a CLASS called c that has three fields: a STRING followed by two INTEGERs. This could be the CLASS of the POINTER shown in Figure 10–1.

CLASS declarations may occur only in the outer declarations of a MODULE (see Chapter 11); i.e., they may not occur within PROCEDUREs.

A field name must not be the same as the name of another field in the same CLASS (or in a prefix CLASS of the CLASS; see Section 9.7) or the same as the CLASS name (or the name of any prefix CLASS of the CLASS). It may have the same name as a local or outer variable or the field of another CLASS. No confusion can occur, since whenever a field name is used in a field variable, it is preceded by a POINTER, inplace record, or ADDRESS that determines its CLASS.

A CLASS c can obtain its initial fields from another CLASS pc by means of a declaration of the form:

CLASS(pcc (declarations of additional fields)

pc is called a prefix CLASS of c, as discussed in Section 9.7.

9.2. Classified POINTERs and ADDRESSes

If a POINTER is to be used only to access dynamic objects of a particular CLASS (the most common case), the name of the CLASS may appear, enclosed in parentheses, following the word POINTER in the POINTER variable declaration. POINTERs so declared are called classified. For example, POINTER(list) p declares a POINTER variable p to reference records that belong to the CLASS called list.

The compiler ensures as best it can that classified POINTERs are not mistakenly used to refer to records of unrelated CLASSes (see Section 9.8), although it cannot do a perfect job; see Section 9.9 and Chapter 18.

9.2.1. Forward CLASSes

A POINTER declaration can use a CLASS that has not yet been declared (sometimes called a forward CLASS). This allows each of two CLASSes to contain a POINTER field to the other. For example, the following declarations:

CLASS a (POINTER(bptrINTEGER i; ...);

CLASS b (POINTER(aqSTRING str; ...)

declare two CLASSes, a and b, each of which has a POINTER field for referencing records belonging to the other CLASS. (Note that when a is declared, b has not yet been declared.)

If the fields of a CLASS are not referenced in a MODULE m, and the CLASS is not used as the CLASS of an inplace record or as the prefix CLASS of another CLASS in m, then the CLASS does not have to be declared in m.

DCL (see Section 16.17) returns FALSE if given a forward CLASS name if its CLASS declaration has not yet been encountered.

9.2.2. Classified ADDRESSes

Like POINTERs, ADDRESSes may be classified. Classified ADDRESSes are not usually used to refer to records allocated by the system PROCEDURE new, but rather to provide a template for storage allocated by a foreign language, or allocated in MAINSAIL scratch space with the system PROCEDURE newScratch, $newScratchChars, or newPage. Syntactically, a classified ADDRESS declaration looks like a classified POINTER declaration, except that the word ADDRESS replaces the word POINTER. The rules for assignment compatibility that apply to classified POINTERs also apply to classified ADDRESSes. You can use a classified ADDRESS as follows:

CLASS c (INTEGER iBITS bADDRESS(clink);
ADDRESS(ca,list;
POINTER(dataFilef;
...
Create a linked list in scratch space
list := NULLADDRESS;
DOB a := newScratch(size(c));
    
read(f,a.i,a.b); a.link := listlist := a;
    ... 
END;

It should not usually be necessary to allocate data in MAINSAIL scratch space; the main reason to do so is to ensure that data passed to a foreign language are not moved by the MAINSAIL memory manager, but that purpose is better achieved by using anchored areas (see Section 25.4).

Since the default layout of fields within a CLASS is subject to change, CLASSes used by classified ADDRESSes as templates for foreign language data structures should be qualified with $ALIGN (see Section 9.10).

9.3. Unclassified POINTERs and ADDRESSes

An unclassified POINTER is useful when the POINTER may refer to a dynamic object of a CLASS that is not known at compiletime. An unclassified ADDRESS is useful to ADDRESS arbitrary memory for which no storage template has been declared.

An unclassified POINTER or ADDRESS declaration omits the parenthesized CLASS name following the keyword POINTER or ADDRESS. For example, POINTER q declares a POINTER variable q that can be used to point to a record of any CLASS, a data section of any MODULE, or an ARRAY of any type, dimension, and bounds. Unclassified POINTERs are considered to be related (see Section 9.8) to all POINTERs in all CLASSes.

9.3.1. Use of Unclassified POINTERs Requires Caution

The programmer must be especially careful when using unclassified POINTERs, since CLASS checking is not currently provided for them (except on SUPERCHECK platforms; see Chapter 18). Using unclassified POINTERs, a classified POINTER p can be made to point at an object that disagrees with p's declaration. Subsequent access to the fields of p can corrupt memory.

The following example shows a POINTER p1 that is made to point at an object of a CLASS (c2) unrelated to the CLASS at which p1 was declared to point (c1) (see Section 9.9 for an example of how an invalid POINTER assignment can occur even without the used of unclassified POINTERs):

POINTER(c1p1;    # assume CLASS c1 is not related to
POINTER(c2p2;    # class c2
POINTER p;

p1 := p2;          # compiler reports a CLASS
                   # 
compatibility error

p := p2;
p1 := p;           # invalidbut compiler does not
                   # 
report an errorand the runtime
                   # 
system does not currently check for
                   # 
this

The effect of the last two statements above would be the same as that of the previous statement p1 := p2. That is, they effectively assign a POINTER (p2) that can access records belonging to the CLASS c2 to a POINTER (p1) that can access records belonging to an unrelated CLASS c1. The consequences of making a POINTER point to a CLASS unrelated to its declaration are undefined. It is the programmer's responsibility to avoid such use of unclassified POINTERs.

Unclassified ADDRESSes are more common than unclassified POINTERs, since a variety of system PROCEDUREs operate on unclassified ADDRESSes.

9.4. Classification of Inplace Records

Unlike a POINTER, where classification is optional, an inplace record must always be classified. The compiler can defer allocation of a dynamic object until runtime, but it must know how to allocate an inplace object at compiletime, so full classification information must be provided with every inplace record declaration.

The CLASS of an inplace record is specified by putting the CLASS of the record in parentheses after the keyword $RECORD, as in:

$RECORD(myClassmyRec;

9.5. Accessing Fields of Records, Data Sections, and Storage Templates

A field of a record, data section, or storage template is accessed by means of a field variable, which has the form x.f where x is called the base part and f the field part. The base and field parts are separated by a period, which need not be immediately adjacent to either part; e.g., x.f could be written x . f (although XIDAK style is always to omit white space before and after the period).

The base part must be a classified POINTER, ADDRESS, or inplace record whose CLASS contains a field named by the field part. The base part may be:

As a special case, it may be a variable declared as an ARRAY rather than as a POINTER or ADDRESS, in which case the field part must be one of the special pseudofields described in Section 12.8.

The field part is the name of a field of the record pointed to by the POINTER base part, of the storage template pointed to by the ADDRESS base part, or of the record denoted by the inplace record base part. The field name must be a field of the CLASS specified in the base part's declaration.

Record fields must be data fields, but data section fields may be either data or PROCEDURE fields. If a data section field is a PROCEDURE (e.g., p.proc(args)), the specified PROCEDURE is invoked in that data section; see Section 11.3.

Example 9–1. The Use of Field Variables
CLASS c2 (INTEGER numSTRING nameBOOLEAN fin);
CLASS c3 (POINTER(c2p);
CLASS c4 (POINTER(c3PROCEDURE proc (INTEGER i));

POINTER(c2p;
POINTER(c3q;
POINTER(c4r;
$RECORD(c2rec1,rec2;
INTEGER t;

...

p := new(c2); # new initializes the fields of the record
              # 
to Zeroi.e., p.num = 0, p.name = "",
              # 
and p.fin = FALSE

Change the fields of the record pointed to by p:
p.num := tp.name := "MAXIMUM"; p.fin := TRUE;

q := new(c3);
q.p := p;     # This is legal and unambiguous; "q.pnow
              # 
refers to the same record as "p".

Change the fields of the record pointed to by p again,
this time going through q:
q.p.num .+ 4; q.p.name .& " 2"; q.p.fin := FALSE;

...

Access fields of inplace records:
IF rec1.fin THEN rec2.name := "xxx";

...

Now change both q and pand invoke proc in r's data
section:
p := (q := r.proc(t)).p;

The data type of a field variable is the data type of the field part. For example, the data type of p.num in Example 9–1 is INTEGER.

The programmer must ensure that the base part a field variable is not Zero (i.e., not NULLPOINTER, NULLADDRESS, or the null $REFERENCE). If checking is in effect (see Section 16.3), code is output for each field variable whose base part is a POINTER, ADDRESS, or $REFERENCE parameter to generate an error if a base part is NULLPOINTER, NULLADDRESS, or the null $REFERENCE. If checking is not in effect, the use of NULLPOINTER, NULLADDRESS, or the null $REFERENCE to access fields has undefined effects.

When the base part of a field variable is itself a field variable, the base part is evaluated first; i.e., a construct such as p.f.g.h is evaluated as ((p.f).g).h.

9.6. Explicit CLASSes

An explicit CLASS can temporarily override the CLASS declared for a POINTER, ADDRESS, or inplace record.

9.6.1. POINTERs and ADDRESSes with Explicit CLASSes

A CLASS name may be specified explicitly for a given use of a POINTER or ADDRESS. The POINTER or ADDRESS is followed by a colon, which is followed by the name of the CLASS. The POINTER or ADDRESS is treated at that point in the program as if it were of the specified CLASS.

The effects of overriding a POINTER's CLASS are undefined if the POINTER does not actually point to a record or data section of the specified CLASS or of a CLASS derived from (prefixed by) the specified CLASS.

9.6.1.1. Use of Explicit CLASSes in POINTER or ADDRESS Field Variables
Explicit CLASSes are useful in field variables whose base part is a POINTER or ADDRESS p to access a field f of the object to which p points when f is not declared in p's CLASS. For example, given the declarations:

CLASS c (...);
CLASS(cd (INTEGER i);
POINTER(cq;

you may know at a given point in the program that q points to a d object, even though it is declared only to point at d's prefix c. In order to access i using q, you must explicitly classify q as being of the CLASS d with the syntax q:d.i, since q.i will not compile (the compiler knows that q's CLASS is c and that c does not contain a field named i).

A redundant explicit CLASS, as in q:c.i, is legal but unnecessary.

Explicitly specifying a CLASS name is required when an unclassified POINTER or ADDRESS is used as the base part of a field variable, since the base part of a field variable must be a classified POINTER or ADDRESS. Specifying a CLASS has the effect of temporarily classifying an unclassified POINTER or ADDRESS.

9.6.1.2. Use of Explicit CLASSes for POINTERs and ADDRESSes outside Field Variables
An explicit CLASS may be specified with a POINTER or ADDRESS even if it is not used as a field variable; this is useful mainly to specify a GENERIC PROCEDURE instance. For example, if a file f is declared as:

POINTER(filef;

then it is permissible to say:

read(f:textFile,...)

to force the GENERIC mechanism to select a textFile form of read. (The effect is undefined if f does not really point to a textFile record.)

9.6.2. Inplace Records with Explicit CLASSes

As with a POINTER or ADDRESS, a CLASS can be specified explicitly for an inplace record. The syntax is r:c, where r is an inplace record and c a CLASS. However, because the compiler generally knows all the details of an inplace record's classification, the situations where an explicit CLASS is useful are fewer than for POINTERs or ADDRESSes.

If a record r is declared to be of a CLASS d, then in r:c, c must be d or a prefix CLASS of d; i.e., r can masquerade as an initial part of itself, but not as a larger record. Violation of this rule is a compiletime error (unlike the corresponding case using POINTERs and ADDRESSes, where no compiletime error would occur).

Explicit classification is useful to assign a prefix of a record. For example, if r1 is a record of c and r2 is a record of d, and c is a prefix of d, then r1 := r2:c assigns the values of r2's c fields to the corresponding fields of r2, and ignores the fields in r2 that are from d but not from c.

9.7. Prefix CLASSes

A CLASS can inherit its initial fields from a previously declared CLASS, called its prefix CLASS. The form of a declaration for such a CLASS is:

CLASS(prefixClassid (declarations of additional fields)

where id is the name of the CLASS being declared (the prefixed CLASS) and prefixClass is the name of its prefix CLASS. The parenthesized declaration list after id may be omitted if there are no additional fields. id is sometimes said to be derived from prefixClass.

The prefix CLASS contributes its fields to the prefixed CLASS. For example, the declarations:

CLASS     c1 (INTEGER iSTRING s);
CLASS(c1c2 (REAL r);

declare two CLASSes, c1 and c2. c1 does not have any prefix CLASSes. c2, a prefixed CLASS, has a prefix CLASS, c1, and has three fields: an INTEGER i, a STRING s, and a REAL r. The first two fields are inherited from c1. Records of the CLASS c2 may be thought of as looking as shown in Figure 9–2.

Figure 9–2. Fields of a Record of a Prefixed CLASS
               - +-----------+ -
              /  | INTEGER i |  \
              |  +-----------+   > fields contributed by c1
fields of c2 <   | STRING s  |  /
              |  +-----------+ -
              \  | REAL r    |  > fields contributed by c2
               - +-----------+ -

Prefix CLASSes permit several related CLASSes to have their initial fields “abstracted out” into a separate CLASS so that records in each of the related CLASSes can be manipulated by PROCEDUREs or statements that access just the initial fields of the common prefix CLASS.

A prefix CLASS may itself have a prefix CLASS. For example, given the declarations:

CLASS    a (...);
CLASS(ab (...);
CLASS(bc (...);

a has no prefix CLASSes, a is the only prefix CLASS of b, and c has two prefix CLASSes, a and b. The fields of c are the fields of a along with the additional fields contributed by b's and c's declarations.

A CLASS may also prefix a MODULE; see Section 11.4.

9.7.1. Accessing Prefix Fields

A POINTER, ADDRESS, or inplace record classified with a prefixed CLASS may be used without an explicit CLASS to access the fields of its prefix CLASSes.

Given these declarations:

CLASS       c1 (INTEGER f);
CLASS(c1)   c2 (STRING  s);
POINTER(c1p1;
POINTER(c2p2;
$RECORD(c2r2;

these field variables are valid:

p1.f  p2.f  p2.s  r2.f  r2.s

and this is invalid:

p1.s

That is, p1 can access fields only in c1, whereas p2 can access fields in both c1 and c2.

If the programmer knows that p1 points to a record of the CLASS c2 or of a prefixed CLASS of c2, then the field variable p1:c2.s can be used to access the field s; thus, the information provided in CLASS declarations may be overridden (see Section 9.6 for details).

9.8. Related CLASSes

Two CLASSes are said to be related if one is a prefix CLASS of the other or if they are the same CLASS. Two POINTERs are said to be assignment compatible (see Section 4.9) if their CLASSes are related or if one or both of them are unclassified. For example, given the declarations:

CLASS        a (...);
CLASS(a)    b1 (...);
CLASS(a)    b2 (...);
CLASS(b1)   c  (...);

each CLASS is related to itself. In addition:

9.9. Safe and Unsafe Assignment of POINTERs

An assignment of the form p := q, where p's CLASS is a prefix CLASS of q's, is considered to be a safe assignment, but an assignment in the opposite direction, i.e., q := p, is considered unsafe. Both are permitted by the compiler since the CLASSes of the POINTERs are related, but the latter allows the programmer subsequently to write syntactically correct but logically invalid field variables, and the assignment itself may sometimes be logically incorrect. This “loophole” in MAINSAIL covers those cases where safe assignments are too restrictive. The following code shows examples of safe and unsafe assignments:

CLASS    a (STRING nameINTEGER num);
CLASS(ab (INTEGER val);
CLASS(ac (STRING sample);

POINTER(apa;
POINTER(bpb;
POINTER(cpc;

pa := new(b);           # Legal

pb := papb.val := 0;  # Validsince pa points to a
                        # 
record of class b

pa := new(a);           # Also legalreplaces previous
                        # 
record pointed to by pa

pb := papb.val := 0;  # Invalidpa points to a record
                        # 
of class a.  There is no "val"
                        # 
field in the record.  Execution
                        # 
of these statements will have
                        # 
undefined consequences.

pc := new(c); pa := pc; # Legal

pb := pa;               # No error at compiletimebut has
                        # 
undefined consequences

pb.val := 0;            # Invalid.  The effect is the same
                        # 
as that of "pc.val := 0", which
                        # 
the compiler would flag as an
                        # 
error.

It is possible that checking code may be emitted by default in a future release of MAINSAIL that would signal an error if an assignment caused a classified POINTER to point to a record not of the POINTER's CLASS or of a prefixed CLASS of the POINTER's CLASS; meanwhile, such a construct has undefined effects on non-SUPERCHECK platforms (see Chapter 18 for a discussion of SUPERCHECK).

9.10. Explicit Alignment of CLASSes

An aligned CLASS is a CLASS that is qualified with the keyword $ALIGN or is prefixed by an aligned CLASS. A record of an aligned CLASS is called an aligned record. Aligned records can be shared with a foreign language because the layout of their fields coincides with that of the foreign language, which is not guaranteed to be true of CLASSes that are not aligned.

If you do not use a CLASS to describe data shared with a foreign language, there is no point in making it an aligned CLASS.

$ALIGN can qualify a CLASS:

$ALIGN CLASS c (...);

This makes c an aligned CLASS. A CLASS declared without the $ALIGN keyword is a nonaligned CLASS.

An aligned CLASS can be used wherever a nonaligned CLASS can be, with the following exceptions:

Different alignments cannot be mixed in the same CLASS. This means that a nonaligned CLASS cannot be used as a prefix of an aligned CLASS or vice versa.

9.10.1. Aligned CLASSes and Foreign Languages

The purpose of explicit alignment is to tell MAINSAIL what layout rules to use for a CLASS to allow sharing of records of that CLASS with a foreign language. Every CLASS used to describe data passed to or from a foreign language should be declared aligned; on the other hand, there is no reason to align CLASSes that are not passed to or from a foreign language.

A different default alignment is used for aligned CLASSes on each platform, based on the foreign language conventions for the platform. If no default alignment is available, the MAINSAIL alignment for the target platform is used. The MAINSAIL alignment is that which is most convenient for MAINSAIL without regard to foreign data access; furthermore, MAINSAIL's alignment rules can change from one release to the next. A record of a nonaligned CLASS should not be used for foreign data access since there is no guarantee that it will be laid out correctly.

9.10.2. Alignment Rules for Each Platform

Normally, the correct alignment rule for the target platform is used automatically by the compiler. This section is only for those who are interested in the exact layout rules on each platform, or who run into a problem with the alignment rules (e.g., a C dialect that does not follow the standard rules for the platform).

For the purposes of the alignment algorithms described below:

Under every current alignment rule, the address alignment of an inplace ARRAY is the same as the address alignment of its elements. Therefore, the rules below refer only to the rules for scalars and inplace records, since the alignment of inplace ARRAYs can be deduced from their elements.

The current alignment algorithms are:

The alignment rules for aligned CLASSes in the Version 16 platforms are given in Table 9–3.

Table 9–3. Alignment Rules for Each Platform
Abbreviation Rule
HPPA ALIGNED
HPPA64 ALIGNED
LINIA64 ALIGNED
LINPN ALIGNED4
NTPNT ALIGNED
PSOLRS ALIGNED
RAIX RAIX
SOLRS ALIGNED
SUN4 ALIGNED

Assuming that LONG REAL occupies 8 bytes, and given the following declarations:

$ALIGN CLASS c1
    (
LONG REAL lrINTEGER(1) i);
$ALIGN CLASS c2
    (
INTEGER(1) j$RECORD(c1rLONG INTEGER(-4) li);

the fields j, r.lr, r.i, and li of c2 would be at the following offsets under the various rules (see Figure 9–4):

Field ALIGNED ALIGNED2 ALIGNED4 RAIX
j 0 0 0 0
r.lr 8 2 4 4
r.i 16 10 12 12
li 24 12 16 20

Figure 9–4. Field Alignment under Different Alignment Rules
<diagram showing offsets of aligned fields>

9.10.3. Assignment Compatibility

Since explicit alignment applies to CLASSes, and only records of the same CLASS are assignment compatible, explicit alignment does not affect the assignment compatibility of records.


previous   next   top   complete contents   complete index   framed top   this page unframed

MAINSAIL Language Manual, Chapter 9