next up previous
Next: 4- Calling Fortran Up: Interfacing Fortran and Previous: 2- External Identifiers

3- Argument Passing in Calling C from Fortran

3.1- Numeric data

Passing numeric arguments between Fortran and C is straightforward. One only has to remember that Fortran always uses call-by-reference, i.e. it passes the address of the memory location where the value is stored.

Consequently, the Fortran routine call

      CALL SUB(X,2)

corresponds to the C function declaration:

void Sub( REAL* xp, INTEGER* ip )

The value itself is accessed as in

  float x = *xp;

To store a value back into the Fortran variable X one has to write in C

  *xp = 3.14;

3.2- Arrays

If X is an array the third element X(3) (in Fortran) has to be addressed as xp[2], since in C array indices start at 0.

It is important to note that the pointers received as arguments must not be used as local variable. For example, one might be tempted to write:

void Sub( REAL* xp, INTEGER* ip )
{
  int i;
  for( i = *ip; i > 0; i-- ) {
    float x = *xp++;
    ...
  }
}

where xp is incremented to point to the next element, which is perfectly legal C. But there is a potential problem if Sub is called from Fortran.

Some machines (e.g. IBM/370 and Convex) use argument blocks, i.e. the call passes the address of a memory region containing the argument addresses. Since a Fortran routine SUB would have no way the alter the content of the argument block, the Fortran compiler treats it as a compile time constant. As a result, only the first call to Sub will access the correct array elements.

3.3- LOGICAL

The representation of .TRUE. varies between platforms. It seems to be safe to test a LOGICAL input against 0 but output parameters should use the proper .TRUE. and .FALSE. constants. Returning the C values ~0 or 0 will work in tests such as

      IF(L) ...
but may fail for

      IF(L .EQ. .TRUE.) ...

3.4- EXTERNAL

C entry points can receive Fortran EXTERNAL arguments as in

      EXTERNAL XT
      CALL SUB(XT)

Most platforms pass directly the address of the routine

typedef void SUBROUTINE();

void Sub( SUBROUTINE* xt )

but some compilers (AIX/370, IBM/370, Apollo, NeXT) store the routine address in a memory cell whose address the called function receives. If we undo the indirection

void Sub( SUBROUTINE** xtp )
{
  SUBROUTINE* xt = *xtp;
  ...
}

in the routine header we can use afterwards in both cases the same C statement:

(*xt)(...)

for calling the routine through the function pointer.

Using a typedef name does more than just improve the readability of the declaration. For the IBM C/370 compiler it is essential to add

#pragma linkage(SUBROUTINE,FORTRAN)
in order to declare that function pointers of type SUBROUTINE have to use the Fortran convention of argument passing.

3.5- CHARACTER

This is one of the most tricky and tedious part of Fortran/C intermixing. A Fortran CHARACTER variable is characterized by two items of information---the address and the length---and the mechanism for passing these two values is compiler dependent. We can label the different mechanisms according to the number of arguments received by the called routine. For a SUBROUTINE with N parameters in total of which k are of type CHARACTER the following number of arguments can be passed:

N
Address and length are passed in a single string descriptor argument.
N+k
The address is passed at the position of the CHARACTER argument and for each CHARACTER argument the length information is added at the end of the regular argument list.
2N
Like the previous case but there is an extra length indicator, for each CALL argument.

With variations on the type of the length indicator these are the three different schemes I have come across up to now. Of course, there are more possible permutations a new Fortran compiler may choose from.

To give a more complete picture we will take the example of the following basic Fortran routine

      SUBROUTINE SUB(X,CH,Y)
      REAL X,Y
      CHARACTER*(*) CH

and give the different headers, according to machines, for the C equivalent function in order to make it Fortran callable without any problem. In each case we will end up in the C function with the fixed variable names char* ch_ptr and int ch_len containing the address and length of CH.

VMS

VMS passes the address of a string descriptor containing the address and length information as structure elements:

#include <descrip.h>

void Sub( REAL* x, 
          struct dsc$descriptor_s* ch_dsc,
          REAL* y )
{
  char* ch_ptr = ch_dsc->dsc$a_pointer;
  int   ch_len = ch_dsc->dsc$w_length;
  ...
}

Cray

Cray packs both items into the 64-bit word received for each argument (and the bit fields used are actually processor dependent):

#include <fortran.h>

void Sub( REAL* x, _fcd ch_dsc, REAL* y )
{
  char* ch_ptr = _fcdtocp(ch_dsc);
  int   ch_len = _fcdlen(ch_dsc);
  ...
}

VS-Fortran

IBM VS-Fortran uses the 2N scheme with call-by-reference for the length information:

#pragma linkage(SUB,FORTRAN)

void Sub( REAL* x,     char* ch_ptr, REAL* y,
     int*  x_size, int* ch_ref, int*  y_size )
{
  int ch_len = *ch_ref;
  ...
}

NeXT

For the Absoft Fortran compiler on NeXT it looks similar except that call-by-value is used:

void Sub( REAL* x,    char* ch_ptr, REAL* y,
     int   x_size, int ch_len, int   y_size )
{
  ...
}

Others

The bulk of Fortran compilers add the length as int only for type CHARACTER:

void Sub( REAL* x, char* ch_ptr, REAL* y,
                     int ch_len )
{
  ...
}

Apollo

The only remaining exception is the Apollo ftn compiler which passes the length in a short*:

void Sub( REAL* x, char* ch_ptr, REAL* y,
                  short* ch_ref )
{
  int ch_len = *ch_ref;
  ...
}

KUIP offers a number of convenience functions (fstrdup, fstrset, ...) to convert between fixed length, blank-filled Fortran CHARACTER buffers and null-terminated, blank-trimmed C strings. For details refer to the KUIP manual.



next up previous
Next: 4- Calling Fortran Up: Interfacing Fortran and Previous: 2- External Identifiers



Janne Saarela
Mon May 22 15:43:10 METDST 1995