MAINSAIL Language Manual, Chapter 8

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


8. PROCEDURE Variables

A PROCEDURE variable (or $PROCVAR) is a data type for representing an invokable PROCEDURE. At runtime, a $PROCVAR can reference different PROCEDUREs at different times, and can invoke the PROCEDURE that it currently references. That is, a $PROCVAR can be used to invoke a PROCEDURE whose identity is unknown at compiletime. All PROCEDUREs that can be referenced by a particular $PROCVAR have the same characteristics (result type, parameter types, and parameter qualifiers), which are specified in the $PROCVAR's declaration.

A $PROCVAR is implemented as two components: a POINTER to a data section and a component that represents the PROCEDURE to be called in the control section corresponding to the data section. The exact layout of the components of a $PROCVAR is not documented because it is subject to change in future releases of MAINSAIL.

8.1. Declaration

A $PROCVAR p is declared as follows:

$PROCVAR(modelProcp;

where modelProc (p's model PROCEDURE) is one of the following:

The PROCEDUREs that p can reference have the same result and parameters as modelProc. If no appropriate declaration is available for modelProc, a FORWARD declaration may be provided just to serve as a model PROCEDURE (note that a body need not be provided for a FORWARD PROCEDURE if it is not called). For convenience, the macro $MODEL is defined as a synonym for FORWARD; it clarifies to the human reader that a PROCEDURE declaration will be used just as a model PROCEDURE.

The form $PROCVAR() p or $PROCVAR p declares an unmodeled $PROCVAR in which the characteristics of the PROCEDUREs that p can reference are not constrained; otherwise a $PROCVAR is modeled. p:modelProc (where modelProc is as described above) may be used to indicate explicitly the model PROCEDURE for an unmodeled $PROCVAR p. An unmodeled $PROCVAR p can be used in a call only in the form p:modelProc, since otherwise the compiler does not know the characteristics of the result and parameters. Unmodeled $PROCVARs should seldom be needed. p:modelProc is unsafe since the compiler does not currently check that the characteristics of p are really as described by modelProc (except on a SUPERCHECK platform; see Chapter 18).

Examples of $PROCVAR declarations:

$MODEL INTEGER PROCEDURE foo (REAL x);
$PROCVAR(fooARRAY(0 TO 10) ary;
$PROCVAR(fooPROCEDURE (INTEGER iproc;

Any variable, field, ARRAY, or PROCEDURE can be of type $PROCVAR.

A $PROCVAR can be a parameter. An OPTIONAL $PROCVAR parameter cannot be declared with an explicit default; i.e., OPTIONAL(v) is not allowed.

INIT is not allowed for a dynamic ARRAY of $PROCVARs.

8.2. Zero

$NULLPROCVAR is the Zero $PROCVAR; its components are Zero. All $PROCVARs (even local variables) are initialized to $NULLPROCVAR.

8.3. Assignment Compatibility

Two $PROCVARs pv1 and pv2 are assignment compatible, and the assignment:

pv1 := pv2

is legal, if either pv1 or pv2 is unmodeled or if they are both modeled and the model PROCEDUREs are compatible, which requires that all the following conditions be satisfied:

A $PROCVAR pv2 passed to a MODIFIES $PROCVAR pv1 must be assignment compatible for both pv1 := pv2 and pv2 := pv1.

A $PROCVAR that has somehow been initialized so it does not reference a PROCEDURE compatible with its declaration results in undefined behavior when it is invoked.

8.3.1. Safe and Unsafe Assignment of $PROCVARs

Assignments involving $PROCVARs can either be safe or unsafe, similar to the way that assignments involving POINTERs, ADDRESSes, or ARRAYs can be safe or unsafe. A legal assignment of $PROCVAR pv2 to $PROCVAR pv1 is safe if any of the following conditions is satisfied:

The notion of safe assignments involving $PROCVARs is used in determining the legality and the type of the result of an IF expression whose result expressions are $PROCVARs, as discussed in Section 4.5.

8.3.2. Examples of Safe and Legal $PROCVAR Assignments

Given the following declarations:

CLASS c (INTEGER i);
CLASS(cd (STRING s);

$MODEL POINTER(cPROCEDURE returnC;
$MODEL POINTER(dPROCEDURE returnD;
$MODEL POINTER PROCEDURE returnUnclassified;
$MODEL PROCEDURE cParm (POINTER(cp);
$MODEL PROCEDURE dParm (POINTER(dp);
$MODEL PROCEDURE uParm (POINTER p);
$MODEL PROCEDURE cProdParm (PRODUCES POINTER(cp);
$MODEL PROCEDURE dProdParm (PRODUCES POINTER(dp);
$MODEL PROCEDURE uProdParm (PRODUCES POINTER p);
$MODEL PROCEDURE cRefParm ($REFERENCE $RECORD(cp);
$MODEL PROCEDURE dRefParm ($REFERENCE $RECORD(dp);
$MODEL PROCEDURE untypedRefParm ($REFERENCE p);

POINTER(cpc;
POINTER(dpd;
POINTER up;
$PROCVAR unm;
$PROCVAR(returnCprc;
$PROCVAR(returnDprd;
$PROCVAR(returnUnclassifiedpru;
$PROCVAR(cParmpcp;
$PROCVAR(dParmpdp;
$PROCVAR(uParmpup;
$PROCVAR(cProdParmcpp;
$PROCVAR(dProdParmdpp;
$PROCVAR(uProdParmupp;
$PROCVAR(cRefParmcrp;
$PROCVAR(dRefParmdrp;
$PROCVAR(untypedRefParmurp;

the rules above imply the legality and safety of assignments as shown below:

unm := prc; # Legal and safe
prc := unm; # Legal but not safe
            # (
not caught by STRICTCLASSES)

pc := pd;   # Legal
pc := up;   # Legal but not safe
            # (
but not caught by STRICTCLASSES)
pd := pc;   # Legal but not safe
            # (
caught by STRICTCLASSES)
pd := up;   # Legal but not safe
            # (
but not caught by STRICTCLASSES)
up := pc;   # Legal
up := pd;   # Legal

prc := prd; # Legalany PROCEDURE that returns a d POINTER
            # 
also returns a c POINTER
prc := pru; # Legal
prd := prc; # ILLEGAL -- returning a c doesn't necessarily
            # 
mean returning a d
prd := pru; # Legal
pru := prc; # Legal but not safe
            # (
not caught by STRICTCLASSES)
pru := prd; # Legal but not safe
            # (
not caught by STRICTCLASSES)

pcp := pdp; # ILLEGAL -- you can pass any POINTER to c to pcp,
            # 
but pdp's PROCEDURE would require a pdp POINTER
pcp := pup; # Legal
pdp := pcp; # Legal.  You can only pass POINTER(dto pdp,
            # 
which necessarily means you are passing
            # 
POINTER(c)
pdp := pup; # Legal
pup := pcp; # Legal but not safe
            # (
not caught by STRICTCLASSES)
pup := pdp; # Legal but not safe
            # (
not caught by STRICTCLASSES)

cpp := dpp; # Legal.  You can pass a POINTER(cto cppand
            # 
any POINTER(dis also a POINTER(c)
cpp := upp; # Legal but not safe
            # (
not caught by STRICTCLASSES)
dpp := cpp; # ILLEGAL -- runs risk of assigning a POINTER(c)
            # 
that is not really a POINTER(dto a POINTER(d)
dpp := upp; # Legal but not safe
            # (
not caught by STRICTCLASSES)
upp := cpp; # Legal
upp := dpp; # Legal

crp := drp; # ILLEGAL -- not every $RECORD(c$REFERENCE can be
            # 
a $RECORD(d$REFERENCE
crp := urp; # Legal
drp := crp; # Legal
drp := urp; # Legal
urp := crp; # Legal but not safe
            # (
but not caught by STRICTCLASSES)
urp := drp; # Legal but not safe
            # (
but not caught by STRICTCLASSES)

8.4. Operations

A $PROCVAR can be an operand of OR, AND, NOT, =, NEQ, and :=. Two $PROCVARs are considered equal if and only if they represent the same PROCEDURE in the same data section.

A $PROCVAR can be assigned or passed only to an assignment-compatible $PROCVAR.

A $PROCVAR p is used to invoke the PROCEDURE it references by following it with a (possibly empty) argument list: p(...). The argument parentheses are required even if there are no arguments; in the absence of argument parentheses, a $PROCVAR is treated as a variable rather than an invocation. An attempt to use a Zero $PROCVAR in a call results in a NULLPOINTER access if checking is in effect; otherwise, the effect is undefined.

If p is a $PROCVAR:

8.4.1. $PROCVAR-Valued PROCEDUREs

Invoking a PROCEDURE returned by a $PROCVAR-valued PROCEDURE may require extra parentheses; e.g.:

$MODEL PROCEDURE foo (INTEGER i);

$PROCVAR(fooPROCEDURE p;
body

...

p(0); # syntax error

The compiler reports a syntax error for p(0) because the parameterless PROCEDURE p has apparently been given an argument; to invoke p, then invoke the result with argument 0, use (p)(0) or p()(0).

8.5. $PROC

$PROC(localProc) is a $PROCVAR(localProc) that references the local PROCEDURE localProc in the current data section. The compiler ensures that a body for localProc is compiled into the current MODULE so that a $PROCVAR can reference it.

If m is a MODULE with a PROCEDURE field f, $PROC({m.}f) is a $PROCVAR({m.}f) that references the PROCEDURE that would be invoked by {m.}f. m is automatically bound if necessary to obtain a POINTER to the bound data section.

If p is a POINTER(c) and f is a PROCEDURE field of c, $PROC(p.f) is a $PROCVAR(c.f) that references the PROCEDURE that would be invoked by p.f.

A MAINSAIL system PROCEDURE must not be used as an argument to $PROC, or as a modelProc, since what appears to be a PROCEDURE might actually be a variable, macro, or GENERIC PROCEDURE. Even if a given system identifier is a PROCEDURE in the current release, XIDAK reserves the right in future releases to change it to a variable, macro, or GENERIC PROCEDURE, or to add undocumented OPTIONAL parameters (provided that its invocation syntax does not change).

8.6. Initialization

A $PROCVAR pv can be initialized (e.g., by assignment or argument passing) as illustrated by the following assignments:

pv := $NULLPROCVAR; # pv references no PROCEDURE
pv := x;            # x is a $PROCVAR
pv := y(...);       # y is a PROCEDURE or $PROCVAR that returns
                    # 
a $PROCVAR
pv := $PROC(p);     # p is a PROCEDUREwhich pv now references

8.7. $newProcvar

$newProcvar is a PROCEDURE that creates an unmodeled $PROCVAR given a data section and a PROCEDURE name. This PROCEDURE should be used only when $PROC cannot be used. It is unsafe since it allows the formation of a $PROCVAR that references a PROCEDURE of unknown characteristics.

8.8. $PROCVAR Example

Example use of $PROCVARs:

INTEGER PROCEDURE strCompare (STRING s1,s2);
...


PROCEDURE sortStrings (STRING ARRAY(1 TO *)       ary;
                       
OPTIONAL $PROCVAR(strCompare)
                                                  
cmpProc);
BEGIN
INTEGER i,j;
IF NOT cmpProc THEN cmpProc := $PROC(strCompare); # default
. . .
CASE cmpProc(ary[i],ary[j]) OFB
    . . .
END;


INTEGER PROCEDURE alternateStrCompare (STRING s1,s2);
...


sortStrings(ary);   # Uses strCompare to 
compare strings
sortStrings(ary,$PROC(alternateStrCompare));
                    # 
Uses alternateStrCompare instead

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

MAINSAIL Language Manual, Chapter 8