Chapter 8. Interlanguage Calling

You may want to call external procedures written in C, C++, or some other language, or you may need to call a Fortran procedure from one of those languages. This chapter focuses on the interface between Fortran and C/C++.

If your application has source programs written in different languages, you should compile each file separately, with the appropriate compiler, and then load them in a separate step. You can create object files suitable for loading by specifying the -c option on the f90(1) command, which disables the load step and writes the binary file to file.o. For information on the -c option, see “-c” in Chapter 2.

In the following example, the C/C++ compiler and the MIPSpro 7 Fortran 90 compilers produce object files that can be loaded. These files are named main.o and rest.o:

%   cc -c main.c
%   f90 -c rest.f

This chapter provides more details on compiling and loading application programs that are written in Fortran, C, and C++.

External and Public Names

When your Fortran program defines the body of a procedure, the compiler places the name of the procedure, as a character string, in the object file it generates. This is a public name, which is accessible to other object files.

When your Fortran program uses a procedure, the compiler places the name of the procedure in the generated object file. This is an external name, which is used by the object file but not defined in it. Names of common blocks and names of data and procedures declared within object files are also external names. You can use the nm(1) utility to display the public and external names defined in a file. For more information on this utility, see the MIPSpro Compiling and Performance Tuning Guide.

It is up to the IRIX loader, ld(1), to resolve each reference to an external name by finding that same name as a public name in some other module. This is the main job of the loader.

Fortran Treatment of External and Public Names

The Fortran compiler ignores the case of the input source text (other than the contents of character literals). As a result, it may change the case of the names of procedures and named common blocks while it translates the source file. The names recorded in the object file are changed in the following two ways from the way you may have written them:

  • They are converted to all lowercase letters.

  • They are normally extended with a final underscore (_) character.

Procedure names and common block names are translated, too.

The following declarations produce the identifiers matrix_, mixedcase_, and cblk_ in the object file:

SUBROUTINE MATRIX
external function MixedCase()
COMMON /CBLK/a,b,c

These changes cause no problems when loading program units compiled by Fortran. The same convention is used for both the public and external names, so the names match.


Note: Some IRIX-based FORTRAN 77 compilers support the -U command line option, which prevents the compiler from forcing all uppercase input to lowercase. As a byproduct, it becomes possible to put mixed case public names in the object file. This option is not supported by the MIPSpro 7 Fortran 90 compiler.

In addition, some IRIX-based FORTRAN 77 compilers take the use of the $ character as the final letter of a procedure name as a signal to suppress the underscore in the public name. The $ is not permitted to appear in a name if the program is to be compiled by the MIPSpro 7 Fortran 90 compiler. There is no way to suppress the final underscore in an external name.

You can override the default conventions by using the !DIR$ NAME directive described in “External Name Mapping Directive: NAME” in Chapter 6.

Module and internal procedure names are connected with .in. to make a unique name. For example, the following code creates procedures named MPROC.in.MMM and IPROC.in.MPROC.in.MMM:

MODULE MMM
...
CONTAINS
  SUBROUTINE MPROC()
  ...
  CONTAINS
    SUBROUTINE IPROC()
    ...

Calling a Fortran Subprogram from C

To call a Fortran subprogram from a C procedure, spell the name the way the Fortran compiler spells it, using all lowercase letters and a trailing underscore.

For example, consider the following Fortran declaration:

SUBROUTINE HYPOT()

This must be declared in a C function as follows (note the use of lowercase with a trailing underscore):

extern int hypot_()


Note: You cannot call Fortran subroutines that contain assumed-shape dummy arguments or Fortran pointer arguments from C.


Calling a C Function from Fortran

To call a C function from a Fortran program, ensure that the C function's name is spelled the way the Fortran compiler expects it to be. When you control the name of the C function, the simplest solution is to give it a name that consists of lowercase letters with a terminal underscore. For example, the following C function:

int fromfort_() {...}

could be declared in a Fortran program as follows:

external FROMFORT

When you do not control the name of a C function, you must either supply a function name that the Fortran compiler can call or use the !DIR$ NAME directive described in “External Name Mapping Directive: NAME” in Chapter 6. If you choose to supply a function name that the Fortran compiler can call, you need to write a C function that accepts the same arguments and has a name composed of lowercase letters followed by an underscore. This C function can then call the function whose name contains mixed case letters. You can write such a wrapper function manually, or you can use the mkf2c(1) utility to do it automatically.

Correspondence of Fortran and C Data Types

When you exchange data between Fortran and C, either as arguments, as function results, or as members of common blocks, you must make sure that the two languages agree on the size, alignment, and subscript of each data object.

Corresponding Scalar Types

The correspondence between Fortran and C scalar data types is shown in Table 8-1. This table assumes that the default command line options that affect precision are in effect. Certain f90(1) command line options (such as -i2 or -r8) affects storage sizes for integer, logical, real, and double precision data types. For information on the -i2 and -r8 options, see “-in” in Chapter 2, and “-rreal_spec” in Chapter 2.

Table 8-1. Corresponding Fortran and C Data Types

Fortran Data Type Declaration

C Data Type

INTEGER(KIND=1), LOGICAL(KIND=1)

signed char

CHARACTER

unsigned char

INTEGER(KIND=2), LOGICAL(KIND=2)

short

INTEGER, INTEGER(KIND=4), LOGICAL, LOGICAL(KIND=4)

int

INTEGER(KIND=8), LOGICAL(KIND=8)

long long

REAL, REAL(KIND=4)

float

DOUBLE PRECISION, REAL(KIND=8)

double

REAL(KIND=16)

long double

COMPLEX, COMPLEX(KIND=4)

struct{float real, imag;};

DOUBLE COMPLEX, COMPLEX(KIND=8)

struct{double real, imag;};

COMPLEX(KIND=16)

struct{long double real, imag;};

CHARACTER(n)

char fstr_n[n]

For type character, Fortran declarations with a length designator, such as CHARACTER(LEN=N) :: X, are equivalent to a C declaration of unsigned char X[N].

To set a NULL character in a Fortran string, use CHAR(0). Examples:

character*4 aaa
aaa(1:3) = 'abc'
aaa(4:4) = CHAR(0)

Corresponding Character Types

The Fortran CHARACTER data type declaration corresponds to the C type unsigned char. However, the two languages differ in the treatment of strings of characters.

A Fortran variable can be declared as CHARACTER(n), where n>1, contains exactly n characters at all times. When a shorter character expression is assigned to it, it is padded on the right with spaces to reach n characters.

A C vector of characters is normally sized 1 greater than the longest string assigned to it. It may contain fewer meaningful characters than its size allows, and the end of meaningful data is marked by a null byte. There is no null byte at the end of a Fortran string (except by chance memory alignment).

There is no terminal null byte, so most of the string library functions familiar to C programmers (strcpy()(3c), strcat()(3c), strcmp()(3c), and so on) cannot be used with Fortran string values. The strncpy()(3c), strncmp()(3c), bcopy()(3c), and bcmp()(3c) functions can be used because they depend on a count rather than a delimiter.

Unsupported Array Arguments

Fortran supports assumed-shape arrays, deferred-shape arrays, and array sections. You cannot pass any of these types of array to a non-Fortran procedure because Fortran represents such arrays in memory using a descriptor containing indirect pointers and other data. The format of this descriptor is not part of the published programming interface to the MIPSpro 7 Fortran 90 compiler because it is subject to change.

How Fortran Passes Arguments

When calling non-Fortran functions, you must know how arguments are passed. When calling Fortran subprograms from other languages, you must cause the other language to pass arguments correctly.


Note: All compilers for a given version of IRIX use identical conventions for passing arguments. These conventions are documented at the machine instruction level in the MIPSpro Assembly Language Programmer's Guide, which also describes the differences in the conventions used in different releases.

An argument passed to a subprogram, regardless of its data type, is passed as the address of the actual in memory. This rule is extended for two special cases:

  • The length of each CHARACTER(n) declaration is passed as an implicit additional INTEGER(KIND=4) value, following the explicit arguments.

  • When a function returns type CHARACTER(n), the address of the space to receive the result is passed as the first argument to the function, and the length of the result space is passed as the second implicit argument, preceding all explicit arguments.

Example 1. Consider the following code:

COMPLEX(KIND=8) :: CMPLX8
CHARACTER*(16) :: CSTR1, CSTR2
EXTERNAL CPXASC
CALL CPXASC(CSTR1,CSTR2,CMPLX8)

The code generated from the subroutine call in this example passes the following arguments:

  • The address of CSTR1

  • The address of CSTR2

  • The address of CMPLX8

  • The length of CSTR1, an integer value of 16

  • The length of CSTR2, an integer value of 16

Example 2. Consider the following code:

CHARACTER*(8) :: SYMBL,PICKSYM
CHARACTER*(100) :: SENTENCE
INTEGER NSYM
SYMBL = PICKSYM(SENTENCE,NSYM)

The code generated from the function call in the preceding example passes the following arguments:

  • The address of an unnamed result variable

  • The length of an unnamed result variable

  • The address of SENTENCE, the first explicit argument

  • The address of NSYM, the second explicit argument

  • The length of SENTENCE, an integer value of 100

Calling Fortran from C

There are two types of callable Fortran subprograms: subroutines and functions. In C terminology, both types of subprograms are external functions. The difference is the use of the function return value from each.

Calling a Fortran Subroutine from C

From the standpoint of a C function, a Fortran subroutine is an external function returning void.

Example 1. The following example shows a simple Fortran subroutine that adds arrays of complex numbers:

SUBROUTINE ADDC32(Z, A, B, N)
INTEGER :: N
COMPLEX(KIND=16),DIMENSION(N) :: Z,A,B
Z = A + B
RETURN
END SUBROUTINE

The Fortran subroutine could be called from C using the following code fragment:

typedef struct{long double real, imag;} cpx32;
extern void
   addc32_(cpx32 *,cpx32 *,cpx32 *,int *);
cpx32 z[MAXARRAY], a[MAXARRAY], b[MAXARRAY];
...
      int n = MAXARRAY;
      addc32_(&z, &a, &b, &n);

The preceding code fragments show how the Fortran subroutine is named in the C code using lowercase letters and a terminal underscore. This is the way the Fortran compiler spells the public name in the object file.

Example 2. The following subroutine takes assumed-length character arguments:

SUBROUTINE PRT(BEF, VAL, AFT)
CHARACTER*(*) :: BEF, AFT
REAL :: VAL
PRINT *, BEF, VAL, AFT
RETURN
END SUBROUTINE PRT

The following C code prepares CHARACTER(16) values and passes them to the Fortran subroutine:

typedef char fstr_16[16];
extern void
     prt_(fstr_16 *, float *, fstr_16 *,
                  int, int);
main()
{
      float val = 2.1828e0;
      fstr_16 bef,aft;
      strncpy(bef,"Before..........",sizeof(bef));
      strncpy(aft,"...........After",sizeof(aft));
      prt_(bef, &val, aft, sizeof(bef), sizeof(aft));
}

Note that the subroutine call requires five actual arguments: the addresses of the three explicit arguments and the lengths of the two string arguments. In the C code, the string length arguments are generated using sizeof(), which returns the memory size of the typedef fstr_16.

When the Fortran code does not require a specific string length, the C code that calls it can pass an ordinary C character vector, as shown in the following code fragment:

extern int
prt_(char *, float *, char *, int, int);
main()
{
          float val = 2.1828e0;
          char *bef = "Start:";
          char *aft = ":End";
          (void)prt_(bef, &val, aft, strlen(bef), strlen(aft));
}

In this example, the string length implicit argument values are calculated dynamically using strlen().

Calling a Fortran Function from C

A Fortran function that returns a scalar value as its result corresponds exactly to the C concept of a function with an explicit return value. When a Fortran function returns any type shown in Table 8-1, other than CHARACTER(n), where n>1, you can call the function from C and handle its return value exactly as if it were a C function returning that data type.

Example 1. The following function accepts and returns COMPLEX(KIND=8) values.

FUNCTION FSUB8(INP)
COMPLEX(KIND=8) :: INP,FSUB8
FSUB8 = INP
END FUNCTION FSUB8

Although a complex value is declared as a structure in C, it can be used as the return type of a function. The following C code shows how the preceding Fortran function is declared and called:

typedef  struct{ double real, imag; } cpx8;
extern cpx8 fsub8_(cpx8 *);
main()
{
      cpx8 inp = { -3.333, -5.555 };
      cpx8 oup = { 0.0, 0.0 };
      printf("testing fsub8...");
      oup = fsub8_( &inp );
      if ( inp.real == oup.real && inp.imag == oup.imag )
          printf("Ok\n");
      else
          printf("Nope\n");
}

The arguments to a function, like the arguments to a subroutine, are passed as pointers, but the value returned is a value, not a pointer to a value.

Example 2. The following function has a CHARACTER(16) return value.

FUNCTION FS16(J, K, S)
   CHARACTER*(16) :: FS16, S
   INTEGER J, K
   FS16 = S(J:K)
RETURN
END FUNCTION FS16

When a Fortran function returns CHARACTER(n ), where n>1, value, the returned value is not the explicit result of the function. Instead, you must pass the address and length of the result area as the first two arguments of the function, preceding the explicit arguments. This is demonstrated in the following C code:

typedef char fstr_16[16];
extern void
fs16_ (fstr_16 *, int, int *, int *, fstr_16 *, int);
main()
{
      char work[64];
      fstr_16 inp, oup;
      int j = 7;
      int k = 11;
      strncpy(inp,"0123456789abcdef", sizeof(inp));
      fs16_ ( oup, sizeof(oup), &j, &k, inp, sizeof(inp) );
      strncpy(work, oup, sizeof(oup));
      work[sizeof(oup)] = '\0';
      printf("FS16 returns <%s>\n", work);
}

In this example, the address and length of the function result are the first two arguments of the function. Because type fstr_16 is an array, its name, oup, evaluates to the address of its first element. The next three arguments are the addresses of the three named arguments. The final argument is the length of the string argument.

Calling C from Fortran

You can call units of C code from Fortran as if they were written in Fortran, provided that the C modules follow the Fortran conventions for passing arguments. For more information on this, see “How Fortran Passes Arguments”.

When the C function expects arguments passed using other conventions, you normally need to build a wrapper for the C function using the mkf2c(1) command.

Calls to C Functions

The following C function is written to use the Fortran conventions for its name (lowercase with final underscore) and for argument passing:

/*
|| C functions to export the facilities of strtoll()
|| to Fortran programs.  Effective Fortran declaration:
||
|| FUNCTION ISCAN(S,J)
|| INTEGER(KIND=8) :: ISCAN
|| CHARACTER*(*) S
|| INTEGER J
||
|| String S(J:) is scanned for the next signed long value
|| as specified by strtoll(3c) for a "base" argument of 0
|| (meaning that octal and hex literals are accepted).
||
|| The converted long long is the function value, and J is
|| updated to the nonspace character following the last
|| converted character, or to 1+LEN(S).
||
|| Note: if this routine is called when S(J:J) is neither
|| whitespace nor the initial of a valid numeric literal,
|| it returns 0 and does not advance J.
*/
#include <ctype.h> /* for isspace() */
long long iscan_(char *ps, int *pj, int ls)
{
   int  scanPos, scanLen;
   long long ret = 0;
   char wrk[1024];
   char *endpt;
   /* when J>LEN(S), do nothing, return 0 */ 
   if (ls >= *pj)
   {
      /* convert J to origin-0, permit J=0 */
      scanPos = (0 < *pj)? *pj-1 : 0 ;

      /* calculate effective length of S(J:) */
      scanLen = ls - scanPos;

      /* copy S(J:) and append a null for strtoll() */
      strncpy(wrk,(ps+scanPos),scanLen);
      wrk[scanLen] = `\0';

      /* scan for the integer */
      ret = strtoll(wrk, &endpt, 0);

      /*
      || Advance over any whitespace following the number.
      || Trailing spaces are common at the end of Fortran
      || fixed-length char vars.
      */
      while(isspace(*endpt)) { ++endpt; }
      *pj = (endpt - wrk)+scanPos+1;
   }
   return ret;
}

The following Fortran code fragment demonstrates a call to the preceding C function:

EXTERNAL ISCAN
INTEGER(KIND=8) ISCAN
INTEGER(KIND=8) RET
INTEGER J,K
CHARACTER*(50) INP
INP = '1  -99   3141592  0xfff  033 '
J = 0
DO WHILE (J .LT. LEN(INP))
      K = J
      RET = ISCAN(INP,J)
      PRINT *, K,': ',RET,' -->',J
END DO
END

Using Fortran Common Blocks in C Code

A C function can refer to the contents of a common block defined in a Fortran program. The name of the block as given in the COMMON statement is altered as described in “Fortran Treatment of External and Public Names”. (The name is converted to lowercase and extended with an underscore). The name of the blank common is _BLNK__, with one leading underscore and two trailing ones.

To refer to the contents of a common block, take these steps:

  1. Declare a C structure with fields that have the appropriate data types to match the successive elements of the Fortran common block. For information on corresponding data types, see Table 8-1.

  2. Declare the common block name as an external structure of that type.

The following example employs this method:

        INTEGER STKTOP, STKLEN, STACK(100)
        COMMON /WITHC/ STKTOP, STKLEN, STACK

struct fstack {
      int stktop, stklen;
      int stack[100];
}
extern fstack withc_;
int peektop_()
{
      if (withc_.stktop) /* stack not empty */
          return withc_.stack[withc_.stktop-1];
      else...
}

The restrictions on this capability are as follows:

  • You cannot map a common block that contains Fortran pointer-based variables.

  • If the common block contains a variable of Fortran derived type (a structure), ensure that the derived type is declared with the SEQUENCE attribute. Otherwise, its fields may not appear in the expected sequence in memory.

  • When -O3 is in effect, the compiler may split up common blocks. For information on the -O3 option to the f90(1) command, see “-Olevel” in Chapter 2.

Using Fortran Arrays in C Code

A C program can access arrays created in Fortran. The following example illustrates this.

The following Fortran code fragment declares a matrix in a common block and then calls a C subroutine to modify the array:

INTEGER IMAT(10,100), R, C
COMMON /WITHC/ IMAT
R = 74
C = 6
CALL CSUB(C, R, 746)
PRINT *, IMAT(6,74)
END

The following C function stores its third argument in the common array using the subscripts passed in the first two arguments. In the C function, the order of the dimensions of the array are reversed, so the subscript values are reversed to match, and decremented by 1 to provide 0-origin indexing:

extern struct { int imat[100][10]; } withc_;
void csub_(int *pc, int *pr, int *pval)
{
      withc_.imat[*pr-1][*pc-1] = *pval;
}

Calls to C Using LOC and %VAL

You can use the nonstandard intrinsic functions %VAL and LOC to pass arguments in ways other than the standard Fortran conventions described in “How Fortran Passes Arguments”.

Using %VAL

The %VAL function is used in an argument list to cause an argument to be passed by value rather than by reference. Suppose that you need to call a C function having the following prototype in file ti.c:

#includevoid takesint_(int i, char *s, int len)
{
   printf("i: %d\n", i);
   printf("s: %.*s\n", len, s);
}

The first argument to this function is an integer value, not the address of an integer value in memory. You could call this function from the following Fortran code in file ti_f.f:

      CHARACTER(80) SENTENCE
      INTEGER(4) J
      J = 13
      SENTENCE = "Hello, there."
      CALL TAKESINT(%VAL(J), SENTENCE)
      END

The use of %VAL(j) causes the contents of j to be passed, rather than the address of j.

% f90 -n32 ti_f.f ti.c 
ti_f.f:
ti.c:
% ./a.out
i: 13
s: Hello, there.

Using LOC

The LOC function returns the address of its argument. It can be used with %VAL to prevent passing the length of a character value as a hidden argument. In other words, the argument %VAL(LOC(char_var)) passes only the address of char_var. It does not pass the implicit length argument.

Calling Assembly Language from Fortran

You can write modules in MIPS assembly language, following the guidelines in the MIPSpro Assembly Language Programmer's Guide. Procedures in these modules can be called from Fortran. There is only one special consideration.

Operating in assembly language, you can change the operating mode and the rounding mode of the CPU. When running Fortran programs that contain quad precision operations, you must run the compiler in round-to-nearest mode. This mode is in effect by default, so you usually do not need to set it. When writing programs that call your own assembly routines, ensure that this mode is set. For more information, see the swapRM(3c) man page.