Compaq Fortran
User Manual for
OpenVMS Alpha Systems


Previous Contents Index

10.9.9 Handling User-Defined Structures

User-defined derived types in Compaq Fortran and user-defined C structures can be passed as arguments if the following conditions are met:

10.9.10 Handling Scalar Pointer Data

When Compaq Fortran passes scalar numeric data with the POINTER attribute, how the scalar numeric data gets passed depends on whether or not an interface block is provided:

When passing scalar numeric data without the pointer attribute, Compaq Fortran passes the actual data by reference. If the called C function declares the dummy argument for the passed data to be passed by a pointer, it accepts the actual data passed by reference (address) and handles it correctly.

Similarly, when passing scalar data from a C program to a Compaq Fortran subprogram, the C program can use pointers to pass numeric data by reference.

Example 10-11 shows a Compaq Fortran program that passes a scalar (nonarray) pointer to a C function. Variable X is a pointer to variable Y.

The function call to IFUNC1 uses a procedure interface block, whereas the function call to IFUNC2 does not. Because IFUNC1 uses a procedure interface block (explicit interface), the pointer is passed. Without an explicit interface IFUNC2, the target data is passed.

Example 10-11 Calling C Functions and Passing Pointer Arguments

! Pass scalar pointer argument to C. File: SCALAR_POINTER_FUNC.F90 
 
INTERFACE 
  FUNCTION IFUNC1(A) 
  INTEGER, POINTER :: A 
  INTEGER IFUNC1 
  END FUNCTION 
END INTERFACE 
 
INTEGER, POINTER :: X 
INTEGER, TARGET :: Y 
 
Y = 88 
X => Y 
PRINT *,IFUNC1(X)           ! Interface block visible, so pass 
                            ! pointer by reference. C expects "int **" 
 
PRINT *,IFUNC2(X)           ! No interface block visible, so pass 
                            ! value of "x" by reference. C expects "int *" 
PRINT *,Y 
END 

Example 10-12 shows the C function declarations that receive the Compaq Fortran pointer or target arguments from the example in Example 10-11.

Example 10-12 C Functions Receiving Pointer Arguments

/* C functions Fortran 90 pointers. File: SCALAR_POINTER.C  */ 
 
int ifunc1(int **a) { 
    printf("a=%d\n",**a); 
    **a = 99; 
    return 100; 
    } 
 
int ifunc2(int *a) { 
        printf("a=%d\n",*a); 
        *a = 77; 
        return 101; 
} 

The files (shown in Example 10-11 and Example 10-12) might be compiled, linked, and run as follows:


$ CC  SCALAR_POINTER.C
$ FORTRAN SCALAR_POINTER_FUNC.F90
$ LINK/EXECUTABLE=POINTER  SCALAR_POINTER, SCALAR_POINTER_FUNC
$ RUN POINTER
a=88 
         100 
a=99 
         101 
          77 

10.9.11 Handling Arrays

There are two major differences between the way the C and Compaq Fortran languages handle arrays:

Because of these two factors:

Compaq Fortran orders arrays in column-major order. The following Compaq Fortran array declaration for a 2 by 3 array creates elements ordered as y(1,1), y(2,1), y(1,2), y(2,2), y(1,3), y(2,3):


integer y(2,3) 

The Compaq Fortran declaration for a 2 by 3 array can be modified as follows to have the lowest bound 0 and not 1, resulting in elements ordered as y(0,0), y(1,0), y(0,1), y(1,1), y(0,2), y(1,2):


integer y(0:1,0:2) 

The following C array declaration for a 3 by 2 array has elements in row-major order as z[0,0], z[0,1], z[1,0], z[1,1], z[2,0], z[2,1]:


int z[3][2] 

To use C and Compaq Fortran array data:

When passing certain array arguments, if you use an explicit interface that specifies the dummy argument as an array with the POINTER attribute or an assumed-shape array, the argument is passed by array descriptor (see Section 10.1.7).

For information about performance when using multidimensional arrays, see Section 5.4.

Example 10-13 shows a C function declaration for function EXPSHAPE, which prints the passed explicit-shape array.

Example 10-13 C Function That Receives an Explicit-Shape Array

/* Get explicit-shape arrays from Fortran. File: EXPARRAY_FUNC.C */ 
 
void expshape(int  x[3][2]) { 
    int i,j; 
  for (i=0;i<3;i++) 
     for (j=0;j<2;j++) printf("x[%d][%d]=%d\n",i,j,x[i][j]); 
} 

Example 10-14 shows a Compaq Fortran program that calls the C function EXPSHAPE (shown in Example 10-13).

Example 10-14 Compaq Fortran Program That Passes an Explicit-Shape Array

! Pass an explicit-shape array from Fortran to C. File: EXPARRAY.F90 
 
INTEGER :: X(2,3) 
X = RESHAPE( (/(I,I=1,6)/), (/2,3/) ) 
 
CALL EXPSHAPE(X) 
END 

The files (shown in Example 10-13 and Example 10-14) might be compiled, linked, and run as follows:


$ FORTRAN EXPARRAY.F90
$ CC EXPARRAY_FUNC.C
$ LINK/EXECUTABLE=EXPARRAY EXPARRAY, EXPARRAY_FUNC
$ RUN EXPARRAY
x[0][0]=1 
x[0][1]=2 
x[1][0]=3 
x[1][1]=4 
x[2][0]=5 
x[2][1]=6 

For information on the use of array arguments with Compaq Fortran, see Section 10.1.5.

10.9.12 Handling Common Blocks of Data

The following notes apply to handling common blocks of data between Compaq Fortran and C:

The following examples show how C and Compaq Fortran code can access common blocks of data. The C code declares a global structure, calls the f_calc Compaq Fortran function to set the values, and prints the values:


struct S {int j; float k;}r; 
main() { 
f_calc(); 
printf("%d %f\n", r.j, r.k); 
} 

The Compaq Fortran function then sets the data values:


SUBROUTINE F_CALC() 
COMMON /R/J,K 
REAL K 
INTEGER J 
J = 356 
K = 5.9 
RETURN 
END SUBROUTINE F_CALC 

The C program then prints the structure member values 356 and 5.9 set by the Compaq Fortran function.


Chapter 11
Using OpenVMS Record Management Services

This chapter describes how to call OpenVMS Record Management Services (RMS) directly from Compaq Fortran programs. RMS is used by all utilities and languages for their I/O processing, allowing files to be accessed efficiently, flexibly, with device independence, and taking full advantage of the capabilities of the OpenVMS operating system.

You need to know the basic concepts concerning files on OpenVMS systems and system-service calling conventions before reading this chapter. Basic file concepts are covered in the Guide to OpenVMS File Applications, and system-service calling conventions are covered in Chapter 10.

You should also have access to the OpenVMS Record Management Services Reference Manual. Although not written specifically for Compaq Fortran programmers, that manual is the definitive reference source for all information on the use of RMS.

This chapter will help you understand the material in the OpenVMS Record Management Services Reference Manual in terms of Fortran concepts and usage. You should also be able to more fully understand the material in the Guide to OpenVMS File Applications, which covers more areas of RMS in greater detail than this chapter.

The easiest way to call RMS services directly from Compaq Fortran is to use a USEROPEN routine, which is a subprogram that you specify in an OPEN statement. The Compaq Fortran Run-Time Library (RTL) I/O support routines call the USEROPEN routine in place of the RMS services at the time a file is first opened for I/O.

The Compaq Fortran RTL sets up the RMS data structures on your behalf with initial field values that are based on parameters specified in your OPEN statement. This initialization usually eliminates most of the code needed to set up the proper input to RMS Services. If you specify the USEROPEN keyword in the OPEN statement, control transfers to the specified USEROPEN routine that can further change RMS data structures and then call the appropriate RMS services, including SYS$OPEN (or SYS$CREATE) and SYS$CONNECT.

When you use USEROPEN routines, you can take advantage of the power of RMS without most of the declarations and initialization code normally required. Section 11.3 describes how to use USEROPEN routines and gives examples. You should be familiar with the material in Section 11.1 before reading Section 11.3.

11.1 RMS Data Structures

RMS system services have so many options and capabilities that it is impractical to use anything other than several large data structures to provide their arguments. You should become familiar with all of the RMS data structures before using RMS system services.

The RMS data structures are as follows:

The RMS data structures are used both to pass arguments to RMS services and to return information from RMS services to your program. In particular, an auxiliary structure, such as a NAM or XAB block, is commonly used explicitly to obtain information optionally returned from RMS services.

The OpenVMS Record Management Services Reference Manual describes how each of these data structures is used in calls to RMS services. In this section, a brief overview of each block is given, describing its purpose and how it is manipulated in Compaq Fortran programs.

In general, there are six steps to using the RMS control blocks in calls to RMS system services:

  1. Declare the structure of the blocks and the symbolic parameters used in them by including the appropriate definition library modules from the Fortran default library FORSYSDEF.TLB.
  2. Declare the memory allocation for the blocks that you need with a RECORD statement.
  3. Declare the system service names by including the library module $SYSSRVNAM from FORSYSDEF.TLB.
  4. Initialize the values of fields needed by the service you are calling. The structure definitions provided for these blocks in the FORSYSDEF library modules provide only the field names and offsets needed to reference the RMS data structures. You must assign all of the field values explicitly in your Compaq Fortran program.
    Two fields of each control block are mandatory; they must be filled in with the correct values before they are used in any service call. These are the block id (BID, or COD in the case of XABs) and the block length (BLN). These are checked by all RMS services to ensure that their input blocks have proper form.
    These fields must be assigned explicitly in your Compaq Fortran programs, unless you are using the control blocks provided by the Fortran RTL I/O routines, which initialize all control block fields. See Table 11-1 for a list of the control field values provided by the Fortran RTL I/O routines.
  5. Invoke the system service as a function reference, giving the control blocks as arguments according to the specifications in the RMS reference manual.
  6. Check the return status to ensure that the service has completed successfully.

Steps 1-4 are described for each type of control block in Section 11.1.2 to Section 11.1.5. See Section 11.2 for descriptions of steps 5 and 6.

11.1.1 Using FORSYSDEF Library Modules to Manipulate RMS Data Structures

The definition library FORSYSDEF.TLB contains the required Fortran declarations for all of the field offsets and symbolic values of field contents described in the OpenVMS Record Management Services Reference Manual. The appropriate INCLUDE statement needed to access these declarations for each structure is described wherever appropriate in the text that follows.

In general, you need to supply one or more RECORD statements to allocate the memory for the structures that you need. See the OpenVMS Record Management Services Reference Manual for a description of the naming conventions used in RMS service calls. Only the convention for the PARAMETER declarations is described here.

The FORSYSDEF library modules contain several different kinds of PARAMETER declarations. The declarations are distinguished from each other by the letter following the dollar sign ($) in their symbolic names. Each is useful in manipulating field values, but the intended use of the different kinds of PARAMETER declarations is as follows:

For most of the FAB, RAB, NAM, and XAB fields that are not supplied with symbolic values, you will need to supply sizes or pointers. For sizes, you can use ordinary numeric constants or other numeric scalar quantities. To set the maximum record number into the FAB$L_MRN field, you could use the following statement:


  MYFAB.FAB$L_MRN = 5000 

To supply the required pointers, usually from one block to another, you must use the %LOC built-in function to retrieve addresses. To fill in the FAB$L_NAM field in a FAB block with the address of the NAM block that you want to use, you can use the following program fragment:


  INCLUDE '($FABDEF)' 
  INCLUDE '($NAMDEF)' 
  ...
  RECORD /FABDEF/ MYFAB, /NAMDEF/ MYNAM 
  ...
  MYFAB.FAB$L_NAM = %LOC(MYNAM) 

11.1.2 The File Access Block

The File Access Block (FAB) is used for calling the following services:
SYS$CLOSE
SYS$CREATE
SYS$DISPLAY
SYS$ENTER
SYS$ERASE
SYS$EXTEND
SYS$OPEN
SYS$PARSE
SYS$REMOVE
SYS$RENAME
SYS$SEARCH

The purpose of the FAB is to describe the file being manipulated by these services. In addition to the fields that describe the file directly, there are pointers in the FAB structure to auxiliary blocks used for more detailed information about the file. These auxiliary blocks are the NAM block and one or more of the XAB blocks.

To declare the structure and parameter values for using FAB blocks, include the $FABDEF library module from FORSYSDEF. For example:


  INCLUDE '($FABDEF)' 

To examine the fields and values declared, use the /LIST qualifier after the right parenthesis. Each field in the FAB is described at length in the OpenVMS Record Management Services Reference Manual.

If you are using a USEROPEN procedure, the actual allocation of the FAB is performed by the Fortran Run-Time Library I/O support routines, and you only need to declare the first argument to your USEROPEN routine to be a record with the FAB structure. For example:

Calling program:


  ...
  EXTERNAL MYOPEN 
  ...
  OPEN (UNIT=8, ..., USEROPEN=MYOPEN) 
  ...

USEROPEN routine:


  INTEGER FUNCTION MYOPEN(FABARG, RABARG, LUNARG) 
  INCLUDE '($FABDEF)' 
  ...
  RECORD /FABDEF/ FABARG 
  ...

Usually, you need to declare only one FAB block, but some situations you need to use two. For example, the SYS$RENAME service requires one FAB block to describe the old file name and another to describe the new file name. In any of these cases, you can declare whatever FAB blocks you need with a RECORD statement. For example:


  INCLUDE '($FABDEF)' 
  ...
  RECORD /FABDEF/ OLDFAB, NEWFAB 

If you use any of the above service calls without using a USEROPEN routine, you must initialize the required FAB fields in your program. The FAB fields required for each RMS service are listed in the descriptions of the individual services in the OpenVMS Record Management Services Reference Manual. Most services also fill in output values in the FAB or one of its associated blocks. Descriptions of these output fields are also provided with the service descriptions.

In the example programs in the OpenVMS Record Management Services Reference Manual, these initial field values are described as they would be used in MACRO programs, where the declaration macros allow initialization arguments. In each case where the MACRO example shows a field being initialized in a macro, you must have a corresponding initialization at run time in your program.

The OpenVMS Record Management Services Reference Manual contains an example that shows the use of the ALQ parameter for specifying the initial allocation size of the file in blocks:


  !    Program that uses XABDAT and XABDAT_STORE 
   . 
   . 
   . 
 
  MYFAB: $FAB ALQ=500, FOP=CBT, FAC=<PUT>, - 
              FNM=<DISK$:[PROGRAM]SAMPLE_FILE.DAT>, - 
              ORG=SEQ, RAT=CR, RFM=VAR, SHR=<NIL>, MRS=52, XAB=MYXDAT 
 
   .
   .
   .

As described in the section on the XAB$L_ALQ field (in the same manual), this parameter sets the FAB field FAB$L_ALQ. To perform the same initialization in Compaq Fortran, you must supply a value to the FAB$L_ALQ field using a run-time assignment statement. For example:


  MYFAB.FAB$L_ALQ = 500 

The FAB$B_BID and FAB$B_BLN fields must be filled in by your program prior to their use in an RMS service call, unless they have already been supplied by the Compaq Fortran RTL I/O routines. You should always use the symbolic names for the values of these fields; for example:


  INCLUDE '($FABDEF)' 
  ...
  RECORD /FABDEF/ MYFAB 
  ...
  MYFAB.FAB$B_BID = FAB$C_BID 
  MYFAB.FAB$B_BLN = FAB$C_BLN 
  ...
  STATUS = SYS$OPEN( ... ) 
  ...

11.1.3 The Record Access Block

The Record Access Block (RAB) is used for calling the following services:
SYS$CONNECT SYS$READ
SYS$DELETE SYS$RELEASE
SYS$DISCONNECT SYS$REWIND
SYS$FIND SYS$SPACE
SYS$FLUSH SYS$TRUNCATE
SYS$FREE SYS$UPDATE
SYS$GET SYS$WAIT
SYS$NXTVOL SYS$WRITE
SYS$PUT  

The purpose of the RAB is to describe the record being manipulated by these services. The RAB contains a pointer to the FAB used to open the file being manipulated, making it unnecessary for these services to have a FAB in their argument lists. Also, a RAB can point to certain types of XABs.

Using the FOR$RAB Intrinsic Function

To declare the structure and parameter values for using RAB blocks, include the $RABDEF library module from FORSYSDEF. For example:


  INCLUDE '($RABDEF)' 

To examine the fields and values declared, use the /LIST qualifier after the right parenthesis. Each field in the RAB is described at length in the OpenVMS Record Management Services Reference Manual.

If you are using a USEROPEN procedure, the actual allocation of the RAB is performed by the Compaq Fortran Run-Time Library I/O support routines, and you only need to declare the second argument to your USEROPEN routine to be a record with the RAB structure. For example:

Calling program:


  ...
  EXTERNAL MYOPEN 
  ...
  OPEN (UNIT=8, ..., USEROPEN=MYOPEN) 
  ...

USEROPEN routine:


  INTEGER FUNCTION MYOPEN(FABARG, RABARG, LUNARG) 
  ...
  INCLUDE '($RABDEF)' 
  ...
  RECORD /RABDEF/ RABARG 
  ...

If you need to access the RAB used by the Fortran I/O system for one of the open files in your program, you can use the FOR$RAB intrinsic function (do not declare it as EXTERNAL). You can use FOR$RAB even if you did not use a USEROPEN routine to open the file. The FOR$RAB intrinsic function takes a single INTEGER*4 variable (or constant) argument, the unit number of the open file for which you want to obtain the RAB address. The function result is the address of the RAB for that unit.

If you use the FOR$RAB function in your program, you should declare it to be INTEGER*4 if you assign the result value to a variable. If you do not, your program will assume that it is a REAL function and will perform an improper conversion to INTEGER.

To use the result of the FOR$RAB call, you must pass it to a subprogram as an actual argument using the %VAL built-in function. This allows the subprogram to access it as an ordinary Compaq Fortran record argument. For example, the main program for calling a subroutine to print the RAB fields could be coded as follows:


  INTEGER (KIND=4) RABADR, FOR$RAB 
  ...
  OPEN(UNIT=14, FILE='TEST.DAT', STATUS='OLD') 
  ...
  RABADR = FOR$RAB(14) 
  ...
  CALL DUMPRAB(%VAL(RABADR)) 
  ...

If you need to access other control blocks in use by the RMS services for that unit, you can obtain their addresses using the link fields they contain. For example:


  SUBROUTINE DUMPRAB(RAB) 
  ...
  INTEGER (KIND=4)  FABADR 
  INCLUDE '($RABDEF)' 
  RECORD /RABDEF/ RAB 
  ...
  FABADR = RAB.RAB$L_FAB 
  ...
  CALL DUMPFAB(%VAL(FABADR)) 
  ...

In this example, the routine DUMPRAB obtains the address of the associated FAB by referencing the RAB$L_FAB field of the RAB. Other control blocks associated with the FAB, such as the NAM and XAB blocks, can be accessed using code similar to this example.

Usually, you need to declare only one RAB block. Sometimes, however, you may need to use more than one. For example, the multistream capability of RMS allows you to connect several RABs to a single FAB. This allows you to simultaneously access several records of a file, keeping a separate context for each record. You can declare whatever RAB blocks you need with a RECORD statement. For example:


  INCLUDE '($RABDEF)' 
  ...
  RECORD /RABDEF/ RAB1, RABARRAY(10) 

If you use any of the above service calls without using a USEROPEN routine, you must initialize the required RAB fields in your program. The RAB fields required for each RMS service are listed in the descriptions of individual services in the OpenVMS Record Management Services Reference Manual. Most services also fill in output values in the RAB. Descriptions of these output fields are provided with the service descriptions.

In the example programs supplied in the OpenVMS Record Management Services Reference Manual, these initial field values are described as they would be used in MACRO programs, where the declaration macros allow initialization arguments. Thus, in each case where the MACRO example shows a field being initialized in a declaration macro, you must have a corresponding initialization at run time in your program.

For example, the OpenVMS Record Management Services Reference Manual contains an example that shows the use of the RAC parameter for specifying the record access mode to use:


  . 
  . 
  . 
  
  SRC_FAB: 
          $FAB    FAC=<GET>,-           ; File access for GET only 
                  FOP=<SQO>,-           ; DAP file transfer mode 
                  FNM=<SRC:>            ; Name of input file 
  SRC_RAB: 
          $RAB    FAB=SRC_FAB,-         ; Address of associated FAB 
                  RAC=SEQ,-             ; Sequential record access 
                  UBF=BUFFER,-          ; Buffer address 
                  USZ=BUFFER_SIZE       ; Buffer size 
 
  .
  .
  .

In this example, sequential access mode is used. As described in the section on the RAC field (in the same manual), this parameter sets the RAB$B_RAC field to the value RAB$C_SEQ. This means that to perform the same initialization in Fortran, you must supply RAC field values by a run-time assignment statement. For example:


  MYRAB.RAB$B_RAC = RAB$C_SEQ 

The RAB$B_BID and RAB$B_BLN fields must be filled in by your program prior to their use in an RMS service call, unless they have been supplied by the Fortran RTL I/O routines. You should always use the symbolic names for the values of these fields. For example:


  INCLUDE '($RABDEF)' 
  ...
  RECORD /RABDEF/ MYRAB 
  ...
  MYRAB.RAB$B_BID = RAB$C_BID 
  MYRAB.RAB$B_BLN = RAB$C_BLN 
  ...
  STATUS = SYS$CONNECT(MYRAB) 
  ...


Previous Next Contents Index