When you pass data between routines that are not written in the same OpenVMS language, you have to specify how you want that data to be represented and interpreted. You do this by specifying a parameter-passing mechanism.
The calling standard defines three ways to pass data in an argument list. When you code a reference to a non-DEC C procedure, you must know how to pass each argument and write the function reference accordingly.
The following list describes the three argument-passing mechanisms:
When an argument is passed by immediate value, the actual value of the argument is present in the argument list. This is the default argument-passing mechanism for all function references written in DEC C.
When an argument is passed by reference, the address of the argument is present in the argument list. Use the C ampersand operator (&) to pass the address of an argument, or pass a pointer to the argument by value.
When an argument is passed by descriptor, the address of a data structure describing the argument is present in the argument list. From a DEC C program, you pass a descriptor first by creating a structure (struct) that meets the descriptor requirements of the called procedure and then by passing the structure's address with the ampersand operator or by passing a pointer to that structure by value.
The following sections outline each of these parameter-passing mechanisms in more detail.
By default, all values or expressions in a DEC C function's argument list are passed by immediate value (except for X_FLOATING on OpenVMS Alpha systems, which is passed by reference). The expressions are evaluated and the results placed directly in the argument list of the CALL machine instruction.
The following statement declares the entry point of the Set Event Flag SYS$SETEF system service, which is used to set a specific event flag to 1:
/* Declare the function as a function returning type int. */ int SYS$SETEF();
The SYS$SETEF system service call requires one argument-the number of the event flag to be set-to be passed by immediate value. DEC C for OpenVMS Systems converts linker- resolved variable names (such as the entry-point names of system service calls) to uppercase. You do not have to declare them in uppercase in your program (unless you compile your module with /NAMES=AS_IS). However, linker-resolved variable names must be declared and used with identical cases in each module. The documentation uses uppercase as a convention for referring to system service calls to highlight them in the text and examples.
DEC C does not require you to declare a function or to specify the number or types of the function's arguments. However, if you call a function without declaring it or without providing argument information in the declaration, DEC C does not check the types of the arguments in a call to that function. If you declare a function prototype, the compiler does check the arguments in a call to make sure that they have the same type. (See the DEC C Language Reference Manual for more information on function prototypes.)
Like all system services, SYS$SETEF returns an integer value (the return status of the service) in register 0. Most system services return an integer completion status; therefore, the system service does not always have to be declared before it is used. The examples in this chapter declare system services for completeness.
In the OpenVMS System Services Reference Manual, you can find the specification of each service's arguments. SYS$SETEF, for example, takes one argument, an event flag number. It returns one of four status values, which are represented by the symbolic constants shown in Table 3-3.
Returned | Status | Description |
---|---|---|
SS$_WASCLR | Success | Flag was previously clear |
SS$_WASSET | Success | Flag was previously set |
SS$_ILLEFC | Failure | Illegal event flag number |
SS$_UNASEFC | Failure | Event flag not in associated cluster |
The system services manual also defines event flags as integers in the range 0 to 127, grouped in clusters of 32. Clusters 0 and 1, comprising flags 0 to 31 and 32 to 63, respectively, are local clusters available to any process, with the restriction that flags 24 to 31 are reserved for use by the OpenVMS system. There are many ways of passing valid event flag numbers from your DEC C program to SYS$SETEF. One way is to use enum to define a subset of integers, as follows:
enum cluster0 {completion, breakdown, beginning} event;
After the flag numbers are defined, call the SYS$SETEF service with the following code:
. . . int status; event = completion; . . . status = SYS$SETEF(event); /* Set event flag. */ . . .
Figure 3-4 shows an argument being passed by immediate value; in this case, the event flag number passed to SYS$SETEF.
Since argument lists consist of longwords, the calling standard dictates that immediate-value arguments be expressed in 32 bits. A single-precision, floating-point (F_floating) value is only 32 bits long, but the compiler promotes all arguments of type float to double (64 bits on a VAX processor) unless a function prototype declaration is used for the called function. This double-precision value is passed as two immediate values (two longwords).
On rare occasions, the float-to-double promotion requires some additional programming. For instance, the function OTS$POWRJ, in the VAX Common Run-Time Procedure Library, computes the value of a floating-point number raised to the power of a signed longword (in C terms, a float to the power of an int). This function (and others like it) is called implicitly by high-level OpenVMS languages that have an exponentiation operator as part of the language. It requires that both its arguments be passed as immediate values, and it returns a single-precision (float) result. To pass a floating- point base to the procedure, you must use some method to avoid promoting float arguments. The recommended method is to declare the procedure using a function prototype declaration, as shown in Example 3-1.
/* This program shows how to pass a floating-point value, * * using prototypes to avoid promoting floating * * arguments to arguments of type double. */ #include <stdio.h> /* This declared function returns a value of type float. It * * should be called as follows: OTS$POWRJ(base, power), * * where base is of type float and power is of type int. */ float OTS$POWRJ(float, int); main(void) { /* To hold result of * * OTS$POWRJ */ float result; int power; /* Power argument */ float base; base = 3.145; /* Assign constant to base */ power = 2; result = OTS$POWRJ(base, power); printf("Result= %f\n", result); }
The example does not show the methods for handling arithmetic errors that result from the operation performed. For more information on error handling in this context, and on the run-time library in general, see the VMS Run-Time Library Routines Volume.
When you pass a parameter by value, you pass a copy of the parameter value to the routine instead of passing its address. Because the actual value of the parameter is passed, the routine does not have access to the storage location of the parameter; therefore, any changes that you make to the parameter value in the routine do not affect the value of that parameter in the calling routine.
Some system services and run-time library procedures expect arguments passed by reference. This means that the argument list contains the address of the argument rather than its value. This mechanism is also used by default by some programming languages, such as PL/I, and is available as an option in others, such as Pascal.
In C, you can use the ampersand operator (&) to pass an argument by reference; that is, the ampersand operator causes the argument's address to be passed. Note that an array name without brackets or a function name without parentheses in an argument list always results in passing the address of the array or function; the ampersand is unnecessary. You can also pass a pointer by value, which is the same as passing the item it points to by reference.
In the special case of argument lists, DEC C in VAX C mode allows the ampersand operator to be used on constants as well. You should limit this use of the ampersand solely to calls to OpenVMS system functions to ensure portability of your DEC C programs to other C compilers.
For example, the Read Event Flags (SYS$READEF) system service requires that its first argument be passed by immediate value and its second argument be passed by reference. SYS$READEF returns the status of all the event flags in a particular cluster. (Event flags are numbered from 0 to 127 and arranged in clusters of 32, such that flags 0 to 31 comprise cluster 0, flags 32 to 63, cluster 1, and so forth.)
The first SYS$READEF argument is any event flag number in the cluster of interest. The second argument is the address of a longword that receives the status of all 32 event flags in that cluster. In addition to the event-flag status value, the system service returns one of the status values shown in Table 3-4 expressed as a global symbol.
Returned | Status | Description |
---|---|---|
SS$_WASCLR | Success | Specified event flag was clear |
SS$_WASSET | Success | Specified event flag was set |
SS$_ ACCVIO | Failure | Could not write to status longword |
SS$_ILLEFC | Failure | Event flag number was illegal |
SS$_UNASEFC | Failure | Cluster of interest not accessible |
Example 3-2 shows a call to the SYS$READEF system service from a DEC C program.
/* This program shows how to call system service SYS$READEF. */ #include <ssdef.h> #include <stdio.h> int SYS$READEF(); main(void) { /* Longword that receives * * the status of the * * event flag cluster. */ unsigned cluster_status; int return_status; /* Status: SYS$READEF. */ /* Argument values for * * SYS$READEF. */ enum cluster0 { completion, breakdown, beginning } event; . . . event = completion; /* Event flag in cluster 0. */ /* Obtain status of * * cluster 0. Pass value * * of event and address * * of cluster_status. */
return_status = SYS$READEF(event, &cluster_status); /* Check for successful * * call */ if (return_status != SS$WASCLR && return_status != SS$WASSSET) { /* Handle the error here. */ . . . } else { /* Check bits of interest in cluster_status here. */ . . . } }
A descriptor is a structure that describes the data type, size, and address of a data structure. According to the OpenVMS Calling Standard, you must pass a descriptor by placing its address in the argument list. To pass an argument by descriptor from a DEC C program, perform the following steps:
DEC C never passes arguments by descriptor by default; you must take explicit action to pass an argument by descriptor. Also, if you write structure or union names in a function's argument list without the ampersand operator, the structure or union is passed by immediate value to the called function. You pass arguments by descriptor only when the called function is written in another language and explicitly requires this mechanism.
There are several classes of descriptor. Each class requires that certain bits be set in the first longword of the descriptor. For more information about the descriptors and their formats, see the OpenVMS Programming Interfaces: Calling a System Routine. You can model descriptors in DEC C as follows:
struct dsc$descriptor { unsigned short dsc$w_length; /* Length of data */ char dsc$b_dtype /* Data type code */ char dsc$b_class /* Descriptor class code */ char *dsc$a_pointer /* Address of first byte */ };
In this model, the variable dsc$w_length is a 16-bit word containing the length of the entire data; the unit (for example, bit or byte) in which the length is measured depends on the descriptor class. The member dsc$b_dtype is a byte containing a numeric code; the code denotes the data type of the data. The class member dsc$b_class is another byte code giving the descriptor class. Table 3-5 shows the valid class codes.
Class Code | Symbolic Name | Descriptor Class |
---|---|---|
1 | DSC$K_CLASS_S | Scalar, string |
2 | DSC$K_CLASS_D | Dynamic string descriptor |
3 | - | Reserved by Digital |
4 | DSC$K_CLASS_A | Array |
5 | DSC$K_CLASS_P | Procedure |
6 | DSC$K_ CLASS_PI | Procedure incarnation |
7 | DSC$K_CLASS_J | Reserved by Digital |
8 | DSK$K_ CLASS_JI | This is obsolete |
9 | DSC$K_CLASS_SD | Decimal scalar string |
10 | DSC$K_CLASS_ NCA | Noncontiguous array |
11 | DSC$K_CLASS_VS | Varying string |
12 | DSC$K_CLASS_ VSA | Varying string array |
13 | DSC$K_CLASS_UBS | Unaligned bit string |
14 | DSC$K_ CLASS_UBA | Unaligned bit array |
15 | DSC$K_CLASS_SB | String with bounds descriptor |
16 | DSC$K_CLASS_UBSB | Unaligned bit string with bounds descriptor |
17-190 | - | Reserved by Digital |
191 | DSC$K_CLASS_BFA | Basic file array |
192-255 | - | Reserved for customer applications |
The atomic data types shown in Table 3-6 are supported by DEC C; all others are not directly supported by the language. See the OpenVMS Programming Interfaces: Calling a System Routine manual for a complete list of atomic class codes.
Class Code | Symbolic Name | Descriptor Class |
---|---|---|
2 | DSC$K_DTYPE_BU | Byte (unsigned) |
3 | DSC$K_DTYPE_WU | Word (unsigned) |
4 | DSC$K_ DTYPE_LU | Longword (unsigned) |
6 | DSC$K_DTYPE_B | Byte integer (signed) |
7 | DSC$K_DTYPE_ W | Word integer (signed) |
8 | DSC$K_DTYPE_L | Longword integer (signed) |
10 | DSC$K_DTYPE_ F | F_floating |
11 | DSC$K_DTYPE_D | D_floating |
27 | DSC$K_DTYPE_G | G_ floating |
52 | DSC$K_ DTYPE_FS | IEEE S_floating |
53 | DSC$K_DTYPE_FT | IEEE T_ floating |
The last member of the structure model, dsc$a_pointer, points to the first byte of the data.
To pass an argument by descriptor, you define and assign values to the data following normal C programming practices. You must define a dsc$descriptor structure and assign the data's address to the dsc$a_pointer member. You must also assign appropriate values to the members dsc$w_length, dsc$b_dtype, and dsc$b_class. For the specific requirements of each descriptor class, see the OpenVMS Programming Interfaces: Calling a System Routine manual.
For example, the Set Process Name (SYS$SETPRN) system service, which enables a process to establish or change its process name, accepts a process name as a fixed-length character string passed by descriptor. The character string can have from 1 to 15 characters. The system service returns status values that are represented by the symbolic constants shown in Table 3-7.
Returned | Status | Description |
---|---|---|
SS$_NORMAL | Success | Normal completion |
SS$_ACCVIO | Failure | Inaccessible descriptor |
SS$_DUPLNAM | Failure | Duplicate process name |
SS$_IVLOGNAM | Failure | Invalid length |
Example 3-3 shows a call to this system service from a DEC C program.
/* This program shows a call to system service SYS$SETPRN. */ #include <ssdef.h> #include <stdio.h> /* Define structures for * * descriptors */ #include <descrip.h> int SYS$SETPRN(); int main(void) { int ret; /* Define return status of * * SYS$SETPRN */ /* Name the descriptor */ struct dsc$descriptor_s name_desc; char *name = "NEWPROC"; /* Define new process name */ . . . /* Length of name WITHOUT * * null terminator */ name_desc.dsc$w_length = strlen(name); /* Put address of * * shortened string in * * descriptor */ name_desc.dsc$a_pointer = name; /* String descriptor class */ name_desc.dsc$b_class = DSC$K_CLASS_S; /* Data type: ASCII string */ name_desc.dsc$b_dtype = DSC$K_DTYPE_T; . . . ret = SYS$SETPRN(&name_desc); if (ret != SS$_NORMAL) /* Test return status */ fprintf(stderr, "Failed to set process name\n"), exit(ret); . . . }
In Example 3-3, the call to SYS$SETPRN must use the ampersand operator; otherwise, name_desc, rather than its address, is passed.
Although this example explicitly sets individual fields in its name_ desc string descriptor, in practice, the run-time initialization of compile-time constant string descriptors is not performed in this manner. Instead, the fields of compile-time constant descriptors are usually initialized with initialized structures of storage class static.
For the purpose of string descriptor initialization, DEC C provides a simple preprocessor macro in the <descrip.h> header file. This macro is named $DESCRIPTOR. It takes two arguments, which it uses in a standard DEC C structure declaration. The first argument is an identifier specifying the name of the descriptor to be declared and initialized. The second argument is a pointer to the data byte to be used as the value of the descriptor. Since a character-string constant is interpreted as an initialized pointer to char, you may specify the second argument as a simple string constant. You may use the $DESCRIPTOR macro in any context where a declaration may be used. The scope of the declared string descriptor identifier name is identical to the scope of a simple struct definition as expanded by the macro.
Example 3-4 shows a variant of the program in Example 3-3. Here, the $DESCRIPTOR macro is used to create a compile-time string descriptor and to pass it to the SYS$SETPRN system service routine. In Example 3-4, the program returns the status value returned by SYS$SETPRN to DCL for interpretation.
/* This program returns the status value returned by * * SYS$SETPRN. */ #include <descrip.h> /* Define $DESCRIPTOR * * macro */ int SYS$SETPRN(); main(void) { /* Initialize structure * * name_desc as string * * descriptor */ static $DESCRIPTOR(name_desc,"NEWPROC"); return SYS$SETPRN(&name_desc); }
The $DESCRIPTOR macro is used in further examples in this chapter.
There are default parameter-passing mechanisms established for every data type you can use with DEC C. Table 3-8 lists the DEC C data types you can use with each parameter-passing mechanism. Asterisks appear next to the default parameter-passing mechanism for that particular data type.
Data Type | By Reference | By Descriptor | By Value |
---|---|---|---|
Variables | Yes | Yes | Yes* |
Constants | Yes (VAX C mode only) | Yes | Yes* |
Expressions | No | No | Yes* |
Array elements | Yes | Yes | Yes* |
Entire array | Yes* | Yes | No |
String constants | Yes* | Yes | No |
Structures and unions | Yes | Yes | Yes* |
Functions | Yes* | Yes | No |
You must use the appropriate parameter-passing mechanisms whenever you call a routine written in some other OpenVMS language or some prewritten system routine.