Mixing FORTRAN and C[1]

Calling C Procedures from FORTRAN

C Programs Calling FORTRAN

Compilation

This chapter discusses how a FORTRAN routine can call code that is written in C

and vice versa.

Calling C Procedures from FORTRAN

First, we will discuss how to call C functions from FORTRAN programs. Essentially, this section discusses how to write a C procedure so that a FORTRAN pro gram can call it successfully.

Naming

By default, the FORTRAN program converts function and subroutine names to lowercase and appends an (underscore).  The C compiler never performs any case conversion and does not append an underscore. Therefore, if a FORTRAN program calls a C procedure with the statement:

CALL PROCNAME()

the C procedure must be named procname_. The C procedure may or may not return a value (i.e., its type may be void); any returned value will be ignored. If a FORTRAN program calls a C function with the statement:

X=FNNAME()

then the C procedure must be named fnname_. The C procedure must return a value (i.e., its type must not be void).

Unfortunately, the additional underscore makes it impossible for FORTRAN pro grams to call most UNIX system functions directly. FORTRAN equivalents for the most common system functions are available; they are described in Section 3F of the UNIX Programmer’s Reference Manual. In the more general case, you will have to write your own FORTRAN binding for the system functions that interest you; that is, you will have to write a C routine (with a name ending in an under score) that calls the system function you want. For example, if you want to make the low-level write system call from FORTRAN, you will have to write an intermediate function in C (for example, c_write_) that passes its arguments along to write appropriately.

Remember that names appearing in FORTRAN programs are converted to lower case by default. If the compiler is invoked with the -U option, this conversion does not take place. Capitalization of the functions name in C must match the capitalization used in FORTRAN. Therefore, if the C function’s name has capital letters, you must compile the FORTRAN program with the-U option.

Arguments to Procedures

Scalars

            FORTRAN routines pass arguments by reference; they pass a pointer to the actual argument, rather than the value of the argument themselves. When a FORTRAN program calls a C function, the C function’s arguments must be declared as pointers to the appropriate data type. For the FORTRAN scalar data types INTEGER, INTEGER*2, REAL DOUBLE PRECISION, and LOGICAL, there is a simple correspondence between the type of the FORTRAN actual argument and the type of the argument in the C procedure. The following table shows this correspondence

FORTRAN Type

C Type

INTEGER I

int *i

INTEGER*2 N

short *n

REAL Z

float *z

DOUBLE PRECISION D

double *d

LOGICAL LO

int *lo

For example, the FORTRAN statements:

INTEGER I

INTEGER*2 J

REAL X

DOUBLE PRECISION D

LOGICAL L

CALL VEXP( I, J, X, D, L)

can call a C procedure defined as:

void vexp_( i,  j, x, d, 1)

int *i;

short *j;

float *x;

double *d;

int *1;

{

…Program text...

}

The arguments to the C procedure are declared as pointers because FORTRAN programs pass their arguments as pointers (i.e., by reference) by default and C programs pass arguments by value. Therefore, an argument passed to a C procedure from a FORTRAN program must be declared as a pointer explicitly.  Note that there is not a simple correspondence between CHARACTER data and any C type; procedure calls that pass CHARACTER data are a special case, which we will explain later.

Arrays

For the FORTRAN arrays of types INTEGER, INTEGER*2, REAL, DOUBLE PRE CISION, and LOGICAL, there is a simple correspondence between the type of the FORTRAN actual argument with which a C procedure is called and the type of the argument in the C procedure. The following table shows this correspondence:

FORTRAN Type

C Type

INTEGER X( )

int x[ ]

INTEGER*2 X( )

short x[ ]

REAL X( )

float x[ ]

DOUBLE PRECISION X( )

double x[ ]

LOGICAL X( )

int x[ ]

LOGICAL*2 X( )

short x[ ]

For example, consider the following FORTRAN call:

DIMENSION I(100), X(15O)

CALL ARRAY( I, 100, X, 150)

The C procedure array may be defined as follows:

Array_( i, isize, x, xsize)

int i [ ];

float x[ ];

int *isize, *xsize;

{

…Program text …

}

The C procedure should take these factors into account:

·          FORTRAN and C access arrays differently. FORTRAN organizes arrays in column-major order (i.e., the first dimension of a multiple-dimensioned array varies the fastest). C organizes arrays in row-major order (i.e., the last dimension varies the fastest).

·          FORTRAN array indices start at I, by default; C indices start at 0. Unless you declare the array otherwise, the FORTRAN element X(1) corresponds to the C element x[0].

·          Array arguments to the C procedure do not need to be declared as pointers. Arrays are always passed by reference.

CHARACTER Types

If you pass a CHARACTER argument to a C procedure, the called procedure must be declared with an extra integer argument at the end of its argument list. This argument will be the length of the character variable. An additional integer argument will be added for each CHARACTER argument that is passed to the called procedure. The C type corresponding to CHARACTER is char. For example, consider the following FORTRAN call:

CHARACTER*(*) Cl

CHARACTER*5 C2

REAL X

CALL CHARMAC(Cl, X, C2)

The C procedure corresponding to this call must be declared:

Charmac(c1, x, c2, n1, n2)

 integer n1, n2;

char *c1 *c2;

float *x;

{

…Program text...

}

where ni and ii2 are the lengths of the character strings c1 and c2.  These additional arguments are passed by value, not by reference.

COMPLEX Types

To pass an argument of type COMPLEX or DOUBLE COMPLEX to a C procedure, the corresponding argument in the C procedure must be declared as follows:

            struct { float real, imag; } *complex;

or:

            struct { double real, imag; } *dcomplex;

depending on whether the FORTRAN actual argument is COMPLEX or DOUBLE COMPLEX. For example, the FORTRAN statements:

DOUBLE COMPLEX DC

COMPLEX C

CALL COMPL(DC,C)

call a C routine declared as:

compl( dc, c)

struct { double real, imag;} *dc;

struct { float real, imag; } *c;

{

... Program text...

}

Structures

FORTRAN does not normally have any structured data types. Therefore, it is (strictly speaking) impossible to pass a structure to a C routine. By default, C passes structures by value, which is also something FORTRAN cannot do. There is, however, a workaround that can handle the most important cases. Relatively few C routines actually pass structures by value; most pass a pointer to a structure. Therefore, you can define a common block that matches the structure you want to pass and then use the first element of the common block (which is the base address of the common block) as an argument. For example, the FORTRAN statements:

COMMON /STRUCP/A, I, F, N

CALL CFUNCT(A)

can be used to call cfunct_, defined as:

void cfunct_(s)

struct s_ {

float a;

int i;

float f;

int n;

} *s;

            {

/* text of cfunct_ */

}

Returned Value

For all types except CHARACTER, COMPLEX, and DOUBLE COMPLEX, a C procedure called as a function must return a value of the C type corresponding to whatever type the FORTRAN program expects. The following table shows this correspondence:

FORTRAN Type

C Type

INTEGER*2

short

INTEGER

int x

LOGICAL

int

REAL

float

DOUBLE PRECISION

double

For example, consider these FORTRAN statements:

INTEGER IRET, CFUNCT

IRET    CFUNCT()

The C routine cfunct_ should be declared as:

            int cfunct_();

Functions Returning Character Data

If a FORTRAN program expects a function to return data of type CHARACTER, the FORTRAN compiler adds two additional arguments to the beginning of the called procedure’s argument list. The first of these arguments is a pointer to the location in which the called procedure should store the result, The second is the maximum number of characters that should be returned. The called routine should have type void, which means that its returned value is ignored. It is the called procedure’s responsibility to copy its result through the address specified in the first argument.

 

For example, consider the code:

CHARACTER*10 CHARS, MAKECIIARS

DOUBLE PRECISION X, Y

CHARS=MAKECHARS( X,Y)

TheC routine Makechars_ must be written as follows:

void makechars (result, length, x, y);

char *result;

int length;

double *x, *y;

{

...Program text, producing returnvalue...

for (I = 0; i<length; i++){

            result[i] = returnvalue[i];

}

            }

Note that:

• The arguments length and result do not appear in the FORTRAN statement calling MAICECHARS; they are added by the compiler.

• It is the responsibility of the called routine to copy the result string into the location specified by result. The called routine must not copy more than length characters.

• The called procedure has type void.

Functions Returning Complex Data

If a FORTRAN program expects a procedure to return a COMPLEX or DOUBLE COMPLEX value, the FORTRAN compiler adds an additional argument to the beginning of the called procedures argument list. This additional argument is a pointer to a location at which the called procedure must store its result. The called procedure’s returned value will be ignored. For example, consider the code:

COMPLEX BAT, WBAT

REAL X, Y

BAT=WBAT ( X, Y)

The C routine wbat_ must be written as follows:

void wbat_ (location, x, y);

 struct (float real, imag; } *location;

float *x, *y;

float realpart;

float imaginarypart;

…Program text, producing realpart and   imaginarypart

*location.real = realpart;

*location.imag = imaginarypart;

Again, note that the argument location to what_ does not appear in the FORTRAN call to WBAT; it is added by the compiler. It is the responsibility of the C subrourtine to copy the result’s real and imaginary parts correctly into the location specified by the argument location.

If this were a function returning a DOUBLE COMPLEX value, the type float would be replaced by the type double in the definition of wbat_.

C Programs Calling FORTRAN

When a C routine calls a FORTRAN procedure, it must:

• Convert the procedure’s name to lowercase (unless the FORTRAN code was compiled with the -U option, in which case the capitalization in C and FORTRAN must match).

• Append an (underscore) to the FORTRAN procedure’s name.

•Pass all arguments as pointers.

For example, consider the following C code:

main()

{

extern float ftnfn_();

int in;

float flo;

x = ftnfn_(&in, &flo);

}

This program calls the function FTNFN, which must be defined as:

REAL FUNCTION FTNFN(I,F)

INTEGER I

REAL F

FTNFH = returned-value

END

The following table shows the correspondence between C data types and FORTRAN data types:

C Type

FORTRAN Type

short *x

INTEGER*2 X

int* x

INTEGER*4  X

double*x

DOUBLE PRECISION X

float*x

REAL

struct (float real,imag;); *x

COMPLEX*8 X

struct (Dfloat real,imag;); *x

COMPLEX*16 X

Arrays in C are always passed by reference. The corresponding dummy argument in a FORTRAN subprogram should be an array of the corresponding type.

FORTRAN generally does not have data structures, although some manufactures obsessed with VAX/VMS compatibility may have added them. In general, a C routine cannot return a structure to a FORTRAN caller, nor can C call a FORTRAN program with a structure as an argument. If you need to do this and cannot modify the C program, you must write an intermediate C procedure to handle the transition between C and FORTRAN.

Compilation

The C and FORTRAN compilers can be invoked via either the f77 or the cc command. There is one crucial difference between cc and f77: the two programs mvoke the linker, ld, in different ways. Use cc to invoke the linker if the program’s main, or top-level, routine is written in C; use f77 if the main routine is written in FORTRAN. Using the proper compilation command links the program to the correct initialization and run-time support routines.

 

For example, the following C program calls the FORTRAN subroutine FORTIO:

main ()

{

extern void fortio_();

extern void f_init(););

f_init(); /*initialize the FORTRAN I/O library*/

fortio(); /*perform FORTRAN I/O*/

The following command compiles the FORTRAN and C source files main.c and fio.f, links them with the proper libraries, and generates an executable (a.out) file:

            % cc main.c fio.f –lI77

The program’s main procedure is written in C; therefore, c must be used to link the object modules and produce the executable file The additional -l option ensures the FORTRAN 1/0 library will be present.



[1] Mike Loukides, UNIX for FORTRAN Programmers, O’ Reilly and Associates, Inc, 103 Morris Street, Sebastopol, CA 95472, (1990, 1991) pp 209-218 

I paraphrase a bit, but this is “essentially” from his book.