Kednos PL/I for OpenVMS Systems
User Manual


Previous Contents Index

11.2.1.2 Dummy Arguments for Arguments Passed by Reference

When PL/I passes an argument by reference, it places either the address of the actual argument or the address of a dummy argument in the argument list of the called procedure. PL/I creates a dummy argument in the following cases:

In the last case listed above, PL/I issues an informational or warning message and, for scalar arguments, creates a dummy argument of the data type of the parameter. It places the address of this dummy argument in the argument list. If the argument is an aggregate, PL/I issues an error message; it does not create a dummy argument for an array or for a structure.

In creating a dummy argument, PL/I performs the following conversions:
Data Type of
Written Argument
Data Type of
Dummy Argument
BIT (unaligned) BIT ALIGNED
FIXED BINARY (p,0) or
FIXED DECIMAL (p,0)
FIXED BINARY (31)
CHARACTER VARYING CHARACTER NONVARYING

In all other cases, the data type of the dummy argument is the same as the data type of the written argument.

11.2.1.3 Using Pointer Values for Arguments Passed by Reference

When an argument is passed by reference, PL/I places the address of the actual argument in the argument list. This address can be interpreted as a pointer value. In fact, you can explicitly specify a pointer value as an argument for data to be passed by reference. For example:


DECLARE SYS$READEF (ANY VALUE, POINTER VALUE), 
        FLAGS BIT(32) ALIGNED; 
CALL SYS$READEF (4, ADDR(FLAGS)); 

At this procedure invocation, PL/I places the pointer value returned by the ADDR built-in function directly in the argument list.

Figure 11-5 illustrates the argument list for the following example. Note that the actual argument list in this example corresponds to the argument list shown previously in Figure 11-4. Note also that Figure 11-5 applies to the VAX platform only. Please see OpenVMS Calling Standard for equivalent information for the OpenVMS Alpha platform.


DECLARE FLAGS BIT(32) ALIGNED; 
DECLARE SYS$READEF (ANY VALUE, POINTER VALUE), 
     .
     .
     .
CALL SYS$READEF(4,ADDR(FLAGS)); 

Figure 11-5 Passing a Pointer Value as an Argument (VAX)


11.2.1.4 Passing Arrays to a FORTRAN Routine by Reference

In FORTRAN, arrays must always be passed by reference; the array's extents are, by custom, passed as separate arguments. The REFERENCE attribute provides a convenient way to express an array parameter for FORTRAN. For example:


FTNARRAY: PROCEDURE(X); 
DECLARE SUM ENTRY ((*) FLOAT REFERENCE, FIXED BINARY(31)) 
                    RETURNS (FLOAT); 
 
DECLARE (S, X(*)) FLOAT; 
 
S = SUM(X, DIM(X)); 

SUM is a FORTRAN routine that sums the elements of a one-dimensional array of floating-point numbers. Its second parameter is the number of elements in the array.

11.2.2 Passing Parameters by Descriptor

When you pass a parameter by descriptor, the PL/I compiler passes the address of a descriptor to the called routine. A descriptor is a data structure that contains the address of a parameter, along with other information such as the parameter's data type and size.

For some structure parameters, PL/I passes an abbreviated form of descriptor that contains only essential position and extent information. In these cases, the address of the abbreviated descriptor is placed in the argument list for the called routine. The use of an abbreviated descriptor is transparent to you.

PL/I normally passes parameters by descriptor in the following cases:

For example, PL/I passes the arguments associated with the following parameter descriptors by descriptor:


DECLARE UNSTRING ENTRY (CHARACTER(*)), 
        TESTBITS ENTRY (BIT(3)), 
        MODEST ENTRY (1, 
                        2 CHARACTER(*), 
                        2, 
                          3 BIT(3), 
                          3 BIT(3)); 

Figure 11-6 illustrates a character-string descriptor and shows how a character-string argument is passed by descriptor. This example illustrates the type of character-string descriptor used by system services; this descriptor does not contain additional information required by other classes of descriptors.


DECLARE NAME CHARACTER(5); 
       STATIC INITIAL ('ORION'); 
DECLARE SYS$SETPRN ENTRY 
       (CHARACTER(*)); 
     .
     .
     .
CALL SYS$SETPRN(NAME); 

Figure 11-6 Argument Passing by Descriptor


11.2.2.1 Passing Character Strings

When you declare a non-PL/I routine that requires a character-string descriptor for an argument, specify the parameter descriptor as CHARACTER(*). For example, the Set Process Name system service (SYS$SETPRN) requires the address of a character-string descriptor as an argument. You can declare this service as follows:


DECLARE SYS$SETPRN ENTRY (CHARACTER(*) OPTIONAL); 

When a parameter is declared as CHARACTER(*), its written argument can be one of the following:

For any of these arguments, PL/I constructs a character-string descriptor and places its address in the procedure's argument list.

11.2.2.2 Passing Varying Character Strings

If you specify a varying character-string argument for a parameter declared as (CHARACTER(*), the PL/I compilers issue a warning message, construct a fixed-length character-string dummy argument, and create a character-string descriptor for the dummy argument.

For an input argument, as in the example of the SYS$SETPRN service, the dummy argument is acceptable. To suppress the warning message during compilation, enclose the argument in parentheses. For example, if NAME is a variable declared with the CHARACTER VARYING attributes, you can specify it as an argument to the SYS$SETPRN system service like this:


CALL SYS$SETPRN ((NAME)); 

The parentheses around the argument NAME force PL/I to create a dummy argument. The compiler does not issue a warning about a nonmatching parameter and argument.

For a non-PL/I routine that returns a character string to a variable, however, you cannot use a varying character string for an argument. If the actual output argument is declared as VARYING and the parameter descriptor specifies CHARACTER(*), PL/I creates a dummy argument and the actual argument is not modified. Thus, for output character strings passed by character-string descriptor, you must choose one of the following:

In either case, you must include in your program the statements necessary to determine the length of the string returned. For example, the SYS$ASCTIM system service returns a character-string time value to a character-string descriptor and returns the length of the string to a fixed-point binary variable. These two arguments can be declared as follows:


DECLARE TIME CHARACTER(63), 
        TIME_LENGTH FIXED BINARY(15); 

After the call to this procedure, the following statement might output the equivalence name returned:


PUT LIST ('Time is ',SUBSTR(TIME,1,TIME_LENGTH)); 

The PUT statement uses the SUBSTR built-in function to obtain the length of the string returned in the variable TIME_LENGTH by SYS$ASCTIM.

11.2.2.3 Using ANY CHARACTER(*)

You can use the ANY CHARACTER(*) declaration to declare parameters for routines that can handle both fixed- or varying-length strings for a single parameter. The declaration allows either VARYING or NONVARYING strings to be passed without the creation of a dummy argument. Essentially, all OpenVMS system routines, with the exception of the system services and Librarian Utility routines, can accept string parameters of this type.

Note that routines written in other languages do not allow strings to be passed using this method by default. The routine being called must explicitly use LIB$ANALYZE_SDESC (or an equivalent routine) for this declaration to work correctly.

You can use the ANY CHARACTER(*) attribute as shown in the following example:


%INCLUDE $STSDEF; 
DECLARE FIXED_STRING CHAR(22), 
        VARYING_STRING CHAR(80) VARYING; 
 
DECLARE LIB$DATE_TIME ENTRY( 
            ANY CHARACTER(*)) 
            RETURNS (FIXED BINARY(31)); 
 
STS$VALUE = LIB$DATE_TIME(FIXED_STRING); 
STS$VALUE = LIB$DATE_TIME(VARYING_STRING); 

In both of these cases, the string contains the output value, since no dummy argument is required.

11.2.2.4 Using ANY DESCRIPTOR

The ANY and DESCRIPTOR attributes can be used together for routines that can process any valid data type descriptor. Routines of this type are few; however, LIB$CVT_DX_DX and the routine DTR$COMMAND in the DEC DATATRIEVE layered product callable interface allow any valid data type descriptor. For these routines, declarations should be as follows:


DECLARE LIB$CVT_DX_DX ENTRY( 
                ANY DESCRIPTOR, 
                ANY DESCRIPTOR, 
                FIXED BINARY(15) OPTIONAL TRUNCATE) 
              RETURNS(FIXED BINARY(31)); 
 
DECLARE DTR$COMMAND ENTRY( 
                1 LIKE DTR_ACCESS_BLOCK, 
                ANY CHARACTER(*), 
                ANY DESCRIPTOR LIST TRUNCATE); 

11.2.2.5 Passing an Actual Descriptor

To pass an actual descriptor as an argument, you must take the following steps; the keyed numbers correspond to the callout numbers in Example 11-1.

  1. In the parameter descriptor for the called procedure, declare a structure in the format of a descriptor for the argument that is to be passed by descriptor, specify ANY in the parameter descriptor, or use the REFERENCE built-in function to override the parameter declaration at the point of the call.
  2. Declare a structure variable in your program whose members and attributes correspond to the structure declared in the parameter descriptor for the argument.
  3. Assign values to the members of the structure variable providing the required information. For a character-string descriptor, you must provide the length of the string and a pointer to the variable containing its value.
  4. Pass the name of the structure variable as an argument in the procedure invocation.

The Set Process Name system service (SYS$SETPRN) shown in Example 11-1 requires a text name string to be passed by descriptor. The structure variable NAME_DESC is a character-string descriptor; its members describe the length and location of the character-string variable NEWNAME. The value of NEWNAME is the actual argument passed to the procedure. Note that the call in this example is equivalent to the example shown in Figure 11-6 of passing an argument by descriptor.

Example 11-1 Writing a Character-String Descriptor

DECLARE SYS$SETPRN ENTRY (CHARACTER(*));(1)
 
DECLARE 1 NAME_DESC, 
          2 NAME_LENGTH FIXED BINARY (31),(2)
          2 NAME_ADDRESS POINTER; 
 
DECLARE NEWNAME CHARACTER (5) STATIC INITIAL ('ORION'); 
NAME_DESC.NAME_LENGTH = LENGTH(NEWNAME);(3)
NAME_DESC.NAME_ADDRESS = ADDR(NEWNAME);(3)
 
CALL SYS$SETPRN(REF(NAME_DESC));(4)

Note that this example can be simplified if SYS$SETPRN is declared as follows:


DECLARE SYS$SETPRN ENTRY (ANY); 

All other variables would be the same as in Example 11-1, although the use of the REFERENCE built-in function could be omitted.

In most cases, the first method is preferable because it allows the declarations in PLI$STARLET to be used consistently; the only cases in which special handling is required is for those calls that require it.

11.2.3 Passing Parameters by Value

When you pass a parameter by value, the PL/I compilers pass a copy of the parameter's value to the routine instead of passing its address. Because only a copy of the parameter's value is passed, the routine does not have access to the storage location of the parameter. Therefore, when you pass a parameter by value, any changes that you make to the parameter value in the called routine do not affect the value of that parameter in the calling routine.

For an argument to be passed by value, you must use the VALUE attribute in a parameter description. The following declaration of the external entry VHF illustrates a declaration for an external routine that receives its parameter by value.


DECLARE VHF ENTRY (FIXED BINARY(31) VALUE); 

You can also define PL/I procedures that receive arguments by value. To do this, you must specify the VALUE attribute in the declaration of the parameter. For example, the corresponding definition of the routine VHF would be as follows:


VHF PROCEDURE (LENGTH); 
   .
   .
   .
DECLARE LENGTH FIXED BINARY(31) VALUE; 

The following code example and Figure 11-7 illustrate argument passing by value.


DECLARE VHF ENTRY( 
        FIXED BINARY(31) ANY VALUE); 
    .
    .
    .
CALL VHF(4); 

Figure 11-7 Argument Passing by Immediate Value


Arguments that can be passed by value are limited to the following data types, which can be expressed in 32 bits.

No other data types can be passed by value. Note that when ENTRY VALUE is specified in a parameter descriptor, only the entry points of external routines may be passed by value. A complete entry value for an internal routine requires two longwords, one for the parent frame and one for the 32-bit entry-point address.

When you specify the VALUE attribute in a parameter descriptor, you can specify the ANY attribute instead of declaring any data type attributes. For example, the declaration of VHF can appear as follows:


DECLARE VHF ENTRY (ANY VALUE); 

At the time of the procedure's invocation, PL/I converts the written argument as needed to create a longword dummy argument.

11.2.3.1 Dummy Arguments for Arguments Passed by Value

For arguments to be passed by value, PL/I always creates a dummy argument directly in the argument list for the called procedure. If the parameter descriptor is specified with the ANY and VALUE attributes, dummy arguments are created with the following data types:
Data Type of
Written Argument
Data Type of
Dummy Argument
FIXED BINARY, (p,0) or
FIXED DECIMAL (p,0)
FIXED BINARY (31)
BIT or BIT ALIGNED BIT (32) ALIGNED
ENTRY ENTRY
OFFSET OFFSET
POINTER POINTER

If a parameter descriptor is specified as VALUE with a particular data type (as opposed to being specified as ANY), a dummy argument of that data type is always created, and the written argument is assigned to the dummy. The written argument must be valid for conversion to the data type specified in the corresponding parameter descriptor.

11.2.4 Special Parameter Attributes

PL/I provides the LIST, OPTIONAL, and TRUNCATE parameter attributes.

Each of these attributes is described in the following sections.

11.2.4.1 LIST Attribute

Although most system routines and procedures require a specific number of arguments, some routines accept an unspecified number of optional arguments. To declare these routines in a PL/I program so that you can invoke them with differing numbers of arguments, you must declare the last parameter with the LIST attribute. The last parameter descriptor given in the ENTRY attribute is used for extra arguments.

The Formatted ASCII Output system service (SYS$FAO) is an example of a procedure that has a variable-length argument list. It can be declared as follows:


DECLARE SYS$FAO ENTRY (CHAR(*), FIXED BINARY(15) OPTIONAL, 
                    CHAR(*), ANY VALUE LIST TRUNCATE); 

This parameter descriptor specifies only four arguments. When SYS$FAO is invoked with more than four arguments, PL/I uses the parameter descriptor of the last parameter (ANY VALUE) to pass all the additional arguments. If any argument that will be specified is not to be passed by value, you must specify a parameter descriptor for the argument in the declaration.

Note

The LIST attribute is valid only for parameter descriptors.

11.2.4.2 OPTIONAL Attribute

Some PL/I and non-PL/I routines with fixed-length argument lists accept optional arguments and provide a default action if no value is specified for the optional argument. When an optional argument is not specified, its corresponding argument list longword is filled with a value of zero passed by immediate value.

In PL/I , you can omit the specification of an optional argument in a written argument list as long as you enter the correct number of commas to ensure that the argument list will have the correct number of longwords. You can indicate that you are not specifying an optional argument in either of the following ways:

In the case of trailing arguments all of which have the OPTIONAL attribute, you do not need to specify the commas, although you might choose to do so to add clarity for people who read your source code. But if you do not specify commas, the compiler will automatically create null parameters for trailing formal parameters that have the OPTIONAL attribute, ensuring that any requirement for a minimum length argument vector is met, for instance in the case of VMS system services.

For example, an argument list that has three optional arguments can be written as follows:


(,,) 

The called procedure must detect and interpret the optional parameters in the argument list. The following example illustrates optional arguments omitted from an argument list:


DECLARE SYS$ASCTIM ENTRY ( 
                       FIXED BINARY(15) OPTIONAL, 
                       CHARACTER(*), 
                       ANY OPTIONAL, 
                       BIT(1) ALIGNED VALUE OPTIONAL), 
        TIME_STRING CHARACTER(24); 
   .
   .
   .
CALL SYS$ASCTIM(,TIME_STRING,,); 
   .
   .
   .
CALL SYS$ASCTIM(,TIME_STRING); 

This call to the service SYS$ASCTIM specifies only the second argument; the argument list contains four arguments. When you specify a null argument, as in either of the cases above, PL/I always places a zero in the argument list passed to the called procedure.


Previous Next Contents Index