Chapter 10
Using PL/I in the Common Language Environment
The PL/I compiler is part of the UNIX Standards common language environment. This environment defines certain calling procedures and guidelines that allow you to call routines written in different languages or prewritten system routines from PL/I. You can call any of the following routine types from PL/I:
Routines written in other UNIX languages that conform to the common language environment
UNIX Run-Time Library routines
UNIX system services
UNIX utility routines
The terms routine, procedure, and function are used throughout this chapter.
Important
The definitions of routine, procedure, and function used in this chapter differ somewhat from standard PL/I terminology. The following definitions, used for consistency with the UNIX system routines documentation, apply only to this chapter:
routine—a closed, ordered set of instructions that performs one or more specific tasks. Every routine has an entry point (the routine name), and an optional argument list. Procedures and functions are specific types of routines.
procedure—a routine that does not return a value.
function—routine that returns a value.
system routine—a pre-written UNIX routine that performs a common task, such as finding the square root of a number or allocating virtual memory. You can call any system routine from your program. The system routines used most often are UNIX Run-Time Library routines, utility routines, and system services.
10.1. The UNIX Procedure Calling and Condition Handling Standard
The UNIX Procedure Calling and Condition Handling Standard describes the concepts used by all UNIX languages for invoking routines and passing data between them. This standard specifies the PL/I parameter handling mechanism as well as defining the calling sequence, argument data types and descriptor formats, condition handling, and stack unwinding.
The following sections discuss the parameter handling mechanism in more detail.
To pass data between routines that are not written in the same UNIX language, you must specify how you want that data to be represented and interpreted. You do this by specifying a parameter-passing mechanism. The general parameter-passing mechanisms and their keywords and abbreviations in PL/I are as follows:
By reference—REFERENCE (REF)
By value—VALUE (VAL)
The parameter-passing mechanisms are intended for use only in calling non-PL/I routines. External routines written in PL/I should be called with the established mechanisms.
The following sections describe the arguments in terms of PL/I data types, dummy arguments created (if any), parameter-passing mechanism conventions, and attributes to define the manner in which parameters are to be passed. Remember that when PL/I creates a dummy argument, modifications, if any, that the called procedure makes to the dummy argument are not accessible to the caller.
10.2.1. Passing Parameters by Reference
When you pass a parameter by reference, the PL/I compiler passes the address at which the actual parameter value is stored. In other words, your routine has access to the parameter's storage address. Therefore, when you manipulate and change the value of this parameter, the changed value overwrites the previous value of the parameter. Thus, when you pass a parameter by reference, any changes that you make to the value of the parameter in your routine are reflected in the calling routine as well, provided a dummy argument was not created.
You can pass an argument of any data type by reference. PL/I uses this mechanism by default for all arguments except character strings or arrays with nonconstant extents, and unaligned bit strings. Note that bit-string variables must have the ALIGNED attribute to be passed by reference. In general, the parameter descriptor for an argument to be passed by reference only needs to specify the data type of the parameter.
For example, the MEMSET runtime library routine requires the first argument passed by reference, and the second and third argument passed by value. You can declare this procedure as follows:
DECLARE MEMSET ENTRY (ANY, CHAR(1) VALUE, FIXED BIN(31) VALUE);
When this procedure is invoked, the first parameter can be any variable, the second parameter must be a character, and the third variable a numeric expression.
The data types in the parameter descriptors of all output arguments must match the data types of the written arguments.
10.2.1.1. Using the ANY Attribute
When you write a parameter descriptor for a non-PL/I procedure, you can specify the ANY attribute without the VALUE attribute to describe an argument being passed by reference. The argument can be of any addressable data type known to PL/I. For example:
DECLARE MEMSET ENTRY (ANY, CHAR(1) VALUE, FIXED BIN(31) VALUE);
The first parameter descriptor in the ENTRY attribute specifies that the argument can have any data type and is being passed by reference. When you specify ANY for an argument to be passed by reference, you cannot specify data type attributes. Note that if you specify the VALUE attribute in conjunction with the ANY attribute, PL/I passes the argument by value.
The ANY attribute is especially useful when you must specify a data structure as an argument. You need not declare the structure within the parameter descriptor, only the ANY attribute.
10.2.1.2. Dummy Arguments for Arguments Passed by Reference
When PL/I passes an argument by reference, it puts 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:
When the written argument is a constant or an expression
When the written argument is enclosed in parentheses
When the data type of the written argument does not match the data type or precision specified in the parameter descriptor
In the last case of the previous list, 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 and does not create a dummy argument for the array or structure.
In creating a dummy argument, PL/I does the conversions listed in Table 10.1, “Converted Data Types of Arguments Passed by Reference”:
| 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.
10.2.1.3. Using Pointer Values for Arguments Passed by Reference
When you pass an argument by reference, PL/I puts 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 MEMSET (POINTER VALUE, CHAR(1) VALUE, FIXED BIN(31) VALUE);
When MEMSET is invoked with the following statement at the procedure invocation, PL/I places the pointer value returned by the ADDR built-in function directly in the argument list.
CALL MEMSET(ADDR(EMPLOYEE_RECORD), '', 100);
10.2.1.4. Passing Arrays to a FORTRAN Routine by Reference
In FORTRAN, you must always pass arrays by reference; the array 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.
10.2.2. Passing Parameters by Value
When you pass a parameter by value, the PL/I compiler passes 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;
Arguments that can be passed by value are limited to the following data types, which can be expressed in 32 bits:
FIXED BINARY(m), where m is less than or equal to 31
FLOAT BINARY(n), where n is less than or equal to 24
BIT(p) ALIGNED, where p is less than or equal to 32
ENTRY
OFFSET
POINTER
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);
When the procedure is invoked, PL/I converts the written argument as needed to create a longword dummy argument.
10.2.2.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, PL/I creates dummy arguments the data types shown in Table 10.2, “Assignment of Data Type to Dummy Arguments”:
| 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) |
| 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.
10.2.3. Special Parameter Attributes
PL/I provides the OPTIONS(VARIABLE) option to the ENTRY declaration, and the OPTIONAL and TRUNCATE parameter attributes.
THE OPTIONS(VARIABLE) option indicates that the specified external procedure can be invoked with a variable number of arguments.
The OPTIONAL attribute indicates that the optional arguments will not be specified in the entry invocation.
The TRUNCATE attribute indicates that the argument list may be truncated at a specified position.
The option and parameter attributes are described in the following sections.
10.2.3.1. OPTIONS(VARIABLE) Option
This option is provided for use in calling non-PL/I procedures and can only be used for that purpose. If you declare an external entry with OPTIONS(VARIABLE), you can omit an optional argument and let the compiler pass a zero for the argument. The called procedure usually provides a default value. You must, however, enter the correct number of commas ton ensure that the argument list contains the correct number of longwords.
10.2.3.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:
Omit the argument from the argument list.
If the argument is to be passed by immediate value, specify a zero for the written argument.
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 ASCTIM ENTRY ( FIXED BINARY(15) OPTIONAL, CHARACTER(*), ANY OPTIONAL, BIT(1) ALIGNED VALUE OPTIONAL), TIME_STRING CHARACTER(24); ⋮ CALL ASCTIM(,TIME_STRING,,);
This call to the service ASCTIM specifies only the second argument; the argument list contains four arguments. When you specify a null argument, as in the previous example, PL/I always places a zero in the argument list passed to the called procedure.
10.2.3.3. TRUNCATE Attribute
Some routines may allow an argument list to be truncated in a specified location. For example:
DECLARE GET_INPUT ENTRY (ANY CHARACTER(*), ANY CHARACTER(*) OPTIONAL TRUNCATE, FIXED BINARY(15) OPTIONAL TRUNCATE);
This declaration routine (GET_INPUT) specifies that the argument list may vary in length. In fact, GET_INPUT has three parameters, but the last two parameters are optional.
When you invoke such a routine omitting trailing arguments, you do not have to account for all the arguments in the procedure invocation argument list. For example, the GET_INPUT routine can be invoked with trailing arguments omitted:
CALL GET_INPUT(GET_STRING);The variable GET_STRING is specified for the first argument. The other two arguments are not specified.
10.3. Calling Routines
The basic steps for calling routines are the same whether you are calling a routine written in PL/I, a routine written in some other UNIX language, a system service, a utility routine, or a UNIX Run-Time Library routine. The following sections outline the procedures for calling non-PL/I routines.
10.3.1. Determining the Type of Call
Before you call an external routine, you must first determine whether the call should be a procedure call or a function call. You should call a routine as a procedure if it does not return a value. You should call a routine as a function if it returns any type of value.
10.3.2. Declaring an External Routine and Its Arguments
To call a routine written in another UNIX language, or to call a system routine, you need to declare the routine as an external procedure or function and to declare the data types and passing mechanisms of its arguments. Note that arguments can be either required or optional.
You should include the following information in an external routine declaration:
The name of the routine
The data types of all the routine parameters
The passing mechanisms for all the routine parameters, provided that the routine is not written in PL/I
10.3.3. Calling the External Routine
Once you have declared a routine, you can call it. To call a procedure, you use the CALL statement. To call a function, you can use a function invocation either in an assignment statement or as an argument in another routine call. In either case, you must specify the name of the routine being called and all parameters required for that routine. Make sure the data types and passing mechanisms for the actual parameters you are passing coincide with those you declared earlier, and with those declared in the routine.
If you do not want to specify a value for a required parameter, you can pass a null argument by inserting a comma (,) placeholder in the argument list. If you use any passing mechanism other than the default, you must specify the passing mechanism in the CALL statement or the function call.
At this point, the routine being called receives control, executes, and then returns control to the calling routine at the next statement after the CALL statement or function call.