To understand how to pass arguments between VAX FORTRAN and VAX C programs, it is necessary to understand the possible methods that VMS can use for passing arguments and how each language makes use of them. VMS defines a procedure calling standard that is used by all compilers written by DEC for the VMS operating system. This is described in the ``Introduction to the VMS Run-Time Library'' manual with additional information in the ``Introduction to VMS System Services'' manual. If you have a third party compiler that does not conform to this standard then you will not be able to mix the object code that it produces with that from DEC compilers. There are three ways that an actual argument may be passed to a subroutine. What is actually passed as an argument should always be a longword. It is the interpretation of that longword that is where the differences arise. Note the word should in the last but one sentence. VAX C will occasionally generate an argument that is longer than one longword. This is a violation of the VAX procedure calling standard. It causes no problems for pure VAX C programs, but is a potential source of problems for mixed language programs.
VAX FORTRAN passes all data types other than CHARACTER by reference, i.e. the address of the variable or array is put in the argument list. CHARACTER variables are passed by descriptor. The descriptor contains the type and class of descriptor, the length of the string and the address where the characters are actually stored.
VAX C uses call by value to pass all variables, constants (except string constants), expressions, array elements, structures and unions that are actual arguments of functions. It uses call by reference to pass whole arrays, string constants and functions. VAX C never uses call by descriptor as a default method of passing arguments.
To pass a VAX C variable of type double by value requires the use of two longwords in the argument list and so is a violation of the VAX procedure calling standard. The passing of a VAX C structure that is bigger that one longword is a similar violation. It is always better to pass C structures by reference, although this should not be a problem in practice since in the case of a pure VAX C program, everything is handled consistently and in the case of a mixture of FORTRAN and C, you would not normally pass variables by value anyway.
In VAX FORTRAN, the default argument passing mechanism can be overridden by use of the %VAL, %REF and %DESCR functions. These functions are not portable and should be avoided whenever possible. The only exception is that %VAL is used in Starlink software for passing pointer variables. In VAX C there is no similar way of ``cheating'' as there is in VAX FORTRAN; however, this is not necessary as the language allows more flexibility itself. For example, if you wish to pass a variable named x by reference rather than by value, you simply put &x as the actual argument instead of x. To pass something by descriptor, you need to construct the appropriate structure and pass the address of that. See the DEC manual ``Guide to VAX C'' for further details.
Since C provides more flexibility in the mechanism of passing arguments than does FORTRAN, it is C that ought to shoulder the burden of handling the different mechanisms. All numeric variables and constants, array elements, whole arrays and function names should be passed into and out of C functions by reference. Numeric expressions will be passed from VAX FORTRAN to VAX C by reference and so the corresponding dummy argument in the C function should be declared to be of type ``pointer to type''. When C has a constant or an expression as an actual argument in a function call, it can only pass it by value. VAX FORTRAN cannot cope with this and so in a VAX C program, all expressions should be assigned to variables before being passed to a FORTRAN routine.
Here are some examples to illustrate these points.
PROGRAM FORT1 INTEGER A REAL B A = 1 B = 2.0 CALL C1( A, B ) ENDC function:
void c1( int *a, float *b ) { int x; float y; x = *a; /* x is now equal to 1 */ y = *b; /* y is now equal to 2.0 */ printf( "x = %d\n", x ); printf( "y = %f\n", y ); }
In this first example, a FORTRAN program passes an INTEGER and REAL variable to a C function. The values of these arguments are then assigned to two local variables. They could just as well have been used directly in the function by referring to the variables *a and *b instead of assigning their values to the local variables x and y. Since the VAX FORTRAN program passes the actual arguments by reference, the dummy arguments used in the declaration of the VAX C function should be a pointer to the variable that is being passed.
Now an example of calling a VAX FORTRAN subroutine from VAX C.
main() { int i = 2; /* Declare i and initialize it. */ void fort2( int *i ); /* Declare function fort2. */ fort2( &i ); /* Call fort2. */ }FORTRAN subroutine:
SUBROUTINE FORT2( I ) INTEGER I PRINT *,I END
The VAX C main function declares and initializes a variable, i, and declares a function fort2. It calls fort2, passing the address of the variable i rather than its value, as this is what the VAX FORTRAN subroutine will be expecting.
As we have seen, the case of scalar numeric arguments is fairly straightforward. However, the passing of CHARACTER variables between VAX FORTRAN and VAX C is more complicated. VAX FORTRAN passes CHARACTER variables by descriptor and VAX C must handle these descriptors. Furthermore, there is the point that FORTRAN deals with fixed-length, blank-padded strings, whereas C deals with variable-length, null-terminated strings. It is also worth noting that VAX/VMS machines handle CHARACTER arguments in a manner which is different from the usual Unix way. The simplest possible example of a CHARACTER argument is given here in all of its gory detail. You will be pleased to discover that this example is purely for illustration. The important point is that it is different from the Sun example and, anyway, the F77 macros hide all of these differences from the programmer, thereby making the code portable.
PROGRAM FORT3 CHARACTER STR*20 CALL C3( STR ) PRINT *,STR ENDC function:
#include <descrip.h> /* VMS Descriptors */ #include <stdio.h> /* Standard I/O functions */ void c3( struct dsc$descriptor_s *fortchar ) { int i; /* A loop counter */ char *string = "This is a string"; /* A string to be printed */ /* Copy the string to the function argument */ strncpy( fortchar->dsc$a_pointer, string, fortchar->dsc$w_length ); /* Pad the character argument with trailing blanks */ for( i = strlen( string ) ; i < fortchar->dsc$w_length ; i++ ) fortchar->dsc$a_pointer[i] = ' '; }
The second variable declaration in the C subprogram declares a local variable to be a string and initializes it. This string is then copied to the storage area that the subprogram argument points to, taking care not to copy more characters than the argument has room for. Finally any remaining space in the argument is filled with blanks, the null character being overwritten. You should always fill any trailing space with blanks in this way. What should definitely not be done is to modify the descriptor to indicate the number of non blank characters that it now holds. The VAX FORTRAN compiler will not expect this to happen and it is likely to cause run-time errors. See the DEC manual ``Guide to VAX C'' for more details of handling descriptors in VAX C.
If an actual argument in a VAX FORTRAN routine is an array of characters, rather
than just a single character variable, the descriptor that describes the data
is different. It is defined by the macro dsc$descriptor_a instead of
dsc$descriptor_s. This contains extra information about the number of
dimensions and their bounds; however, this can generally be ignored since the
first part of the dsc$descriptor_a descriptor is the same as the
dsc$descriptor_s descriptor. This extra information can be unpacked from the
descriptor, however, to do so would lead to non-portable code. It is generally
better to use the address of the array that is passed in the descriptor and to
pass any array dimensions as separate arguments. The C subroutine then has all
of the information that it requires and can handle the data as an array or by
using pointers, as the programmer sees fit. See example for
an illustration of this.
CNF and F77 Mixed Language Programming -- FORTRAN and C