previous next top complete contents complete index framed top this page unframed
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.
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:
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:
pc is called a
prefix CLASS of c, as discussed in Section 9.7.
9.1. CLASS Declarations
The most common form of a CLASS declaration is:
CLASS v (declarations of fields of v)
CLASS c (STRING str; INTEGER base,val)
CLASS(pc) c (declarations of additional fields)
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(b) ptr; INTEGER i; ...);
CLASS b (POINTER(a) q; STRING 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 i; BITS b; ADDRESS(c) link);
ADDRESS(c) a,list;
POINTER(dataFile) f;
...
# Create a linked list in scratch space
list := NULLADDRESS;
DOB a := newScratch(size(c));
read(f,a.i,a.b); a.link := list; list := 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.
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):
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.
The CLASS of an inplace record is specified by putting the
CLASS of the record in parentheses after the keyword
$RECORD, as in:
9.3.1. Use of Unclassified POINTERs Requires Caution
POINTER(c1) p1; # assume CLASS c1 is not related to
POINTER(c2) p2; # class c2
POINTER p;
p1 := p2; # compiler reports a CLASS
# compatibility error
p := p2;
p1 := p; # invalid, but compiler does not
# report an error, and the runtime
# system does not currently check for
# this
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.
$RECORD(myClass) myRec;
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 num; STRING name; BOOLEAN fin); CLASS c3 (POINTER(c2) p); CLASS c4 (POINTER(c3) PROCEDURE proc (INTEGER i)); POINTER(c2) p; POINTER(c3) q; POINTER(c4) r; $RECORD(c2) rec1,rec2; INTEGER t; ... p := new(c2); # new initializes the fields of the record # to Zero, i.e., p.num = 0, p.name = "", # and p.fin = FALSE # Change the fields of the record pointed to by p: p.num := t; p.name := "MAXIMUM"; p.fin := TRUE; q := new(c3); q.p := p; # This is legal and unambiguous; "q.p" now # 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 p, and 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(c) d (INTEGER i);
POINTER(c) q;
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.
then it is permissible to say:
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.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(file) f;
read(f:textFile,...)
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(prefixClass) id (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 i; STRING s);
CLASS(c1) c2 (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.
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(a) b (...);
CLASS(b) c (...);
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(c1) p1;
POINTER(c2) p2;
$RECORD(c2) r2;
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:
CLASS a (STRING name; INTEGER num);
CLASS(a) b (INTEGER val);
CLASS(a) c (STRING sample);
POINTER(a) pa;
POINTER(b) pb;
POINTER(c) pc;
pa := new(b); # Legal
pb := pa; pb.val := 0; # Valid, since pa points to a
# record of class b
pa := new(a); # Also legal, replaces previous
# record pointed to by pa
pb := pa; pb.val := 0; # Invalid; pa 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 compiletime, but 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.
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.
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
Assuming that LONG REAL occupies 8 bytes, and given the following
declarations:
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):
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.
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).
Abbreviation Rule HPPA ALIGNED HPPA64 ALIGNED LINIA64 ALIGNED LINPN ALIGNED4 NTPNT ALIGNED PSOLRS ALIGNED RAIX RAIX SOLRS ALIGNED SUN4 ALIGNED
$ALIGN CLASS c1
(LONG REAL lr; INTEGER(1) i);
$ALIGN CLASS c2
(INTEGER(1) j; $RECORD(c1) r; LONG INTEGER(-4) li);
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
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.
MAINSAIL Language Manual, Chapter 9