13.3. Calling Fortran Functions and Subroutines from a C or C++ Function

This subsection describes the following aspects of calling Fortran from C or C++. Topics include requirements and guidelines, argument passing, array storage, logical and character data, accessing named common, and accessing blank common.

13.3.1. Requirements

Keep the following points in mind when calling Fortran functions from C or C++:

13.3.2. Argument Passing

Because Fortran subroutines expect arguments to be passed by pointers rather than by value, C and C++ functions called from Fortran subroutines must pass pointers rather than values.

All argument passing in Cray C is strictly by value. To prepare for a function call between two Cray C functions, a copy is made of each actual argument. A function can change the values of its formal parameters, but these changes cannot affect the values of the actual arguments. It is possible, however, to pass a pointer. (All array arguments are passed by this method.) This capability is analogous to the Fortran method of passing arguments.

In addition to passing by value, Cray C++ also provides passing by reference.

13.3.3. Array Storage

C and C++ arrays are stored in memory in row-major order. Fortran arrays are stored in memory in column-major order. For example, the C or C++ array declaration int A[3][2] is stored in memory as:

Table 13-1.

A[0][0]

A[0][1]

A[1][0]

A[1][1]

A[2][0]

A[2][1]

The previously defined array is viewed linearly in memory as:

A[0][0] A[0][1] A[1][0] A[1][1] A[2][0] A[2][1]

The Fortran array declaration INTEGER A(3,2) is stored in memory as:

Table 13-2.

A(1,1)

A(2,1)

A(3,1)

A(1,2)

A(2,2)

A(3,2)

The previously defined array is viewed linearly in memory as:

A(1,1)  A(2,1)  A(3,1)  A(1,2)  A(2,2)  A(3,2)

When an array is shared between Cray C, C++, and Fortran, its dimensions are declared and referenced in C and C++ in the opposite order in which they are declared and referenced in Fortran. Arrays are zero-based in C and C++ and are one-based in Fortran, so in C and C++ you should subtract 1 from the array subscripts that you would normally use in Fortran.

For example, using the Fortran declaration of array A in the preceding example, the equivalent declaration in C or C++ is:

int a[2][3];

The following list shows how to access elements of the array from Fortran and from C or C++:

Fortran 

C or C++

A(1,1) 

A[0][0]

A(2,1) 

A[0][1]

A(3,1) 

A[0][2]

A(1,2) 

A[1][0]

A(2,2) 

A[1][1]

A(3,2) 

A[1][2]

13.3.4. Logical and Character Data

Logical and character data need special treatment for calls between C or C++ and Fortran. Fortran has a character descriptor that is incompatible with a character pointer in C and C++. The techniques used to represent logical (Boolean) values also differ between Cray C, C++, and Fortran.

Mechanisms you can use to convert one type to the other are provided by the fortran.h header file and conversion macros shown in the following list:

Macro 

Description

_btol  

Conversion utility that converts a 0 to a Fortran logical .FALSE. and a nonzero value to a Fortran logical .TRUE.

_ltob  

Conversion utility that converts a Fortran logical .FALSE. to a 0 and a Fortran logical .TRUE. to a 1.

13.3.5. Accessing Named Common from C and C++

The following example demonstrates how external C and C++ variables are accessible in Fortran named common blocks. It shows a C or C++ C function calling a Fortran subprogram, the associated Fortran subprogram, and the associated input and output.

In this example, the C or C++ structure ST is accessed in the Fortran subprogram as common block ST. The name of the structure and the Fortran common block must match. Note that this requires that the structure name be uppercase. The C and C++ C structure member names and the Fortran common block member names do not have to match, as is shown in this example.

The following Cray C main program calls the Fortran subprogram FCTN:

#include <stdio.h>
struct
{
   int i;
   double a[10];
   long double d;
} ST;

main()
{
   int i;

   /* initialize struct ST */
   ST.i = 12345;

   for (i = 0; i < 10; i++)
      ST.a[i] = i;

   ST.d = 1234567890.1234567890L;

   /* print out the members of struct ST */
   printf("In C: ST.i = %d, ST.d = %20.10Lf\n", ST.i, ST.d);
   printf("In C: ST.a = ");
   for (i = 0; i < 10; i++)
      printf("%4.1f", ST.a[i]);
   printf("\n\n");

   /* call the fortran function */
   FCTN();
}

The following example is the Fortran subprogram FCTN called by the previous Cray C main program:

C *********** Fortran subprogram (f.f): ***********

     SUBROUTINE FCTN

     COMMON /ST/STI, STA(10), STD
     INTEGER STI
     REAL STA
     DOUBLE PRECISION STD

     INTEGER I

     WRITE(6,100) STI, STD
 100 FORMAT ('IN FORTRAN: STI = ', I5, ', STD = ', D25.20)
     WRITE(6,200) (STA(I), I = 1,10)
 200 FORMAT ('IN FORTRAN: STA =', 10F4.1)
     END

The previous Cray C and Fortran examples are executed by the following commands, and they produce the output shown:

% cc -c c.c
% ftn -c f.f
% ftn c.o f.o
% ./a.out
 ST.i = 12345, ST.d = 1234567890.1234567890
 In C: ST.a =  0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0

 IN FORTRAN: STI = 12345, STD = .12345678901234567889D+10
 IN FORTRAN: STA = 0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0

13.3.6. Accessing Blank Common from C or C++

Fortran includes the concept of a common block. A common block is an area of memory that can be referenced by any program unit in a program. A named common block has a name specified in names of variables or arrays stored in the block. A blank common block, sometimes referred to as blank common, is declared in the same way, but without a name.

There is no way to access blank common from C or C++ similar to accessing a named common block. However, you can write a simple Fortran function to return the address of the first word in blank common to the C or C++ program and then use that as a pointer value to access blank common.

The following example shows how Fortran blank common can be accessed using C or C++ source code:

#include <stdio.h>

struct st
{
  float a;
  float b[10];
} *ST;

#ifdef __cplusplus
  extern "C" struct st *MYCOMMON(void);
  extern "C" void FCTN(void);
#else
  fortran struct st *MYCOMMON(void);
  fortran void FCTN(void);
#endif

main()
{
    int i;

    ST = MYCOMMON();
    ST->a = 1.0;
    for (i = 0; i < 10; i++)
        ST->b[i] = i+2;
    printf("\n In C and C++\n");
    printf("     a = %5.1f\n", ST->a);
    printf("     b = ");
    for (i = 0; i < 10; i++)
        printf("%5.1f ", ST->b[i]);
    printf("\n\n");

    FCTN();
}

This Fortran source code accesses blank common and is accessed from the C or C++ source code in the preceding example:

SUBROUTINE FCTN
COMMON // STA,STB(10)
PRINT *, "IN FORTRAN"
PRINT *, "    STA = ",STA
PRINT *, "    STB = ",STB
STOP
END

FUNCTION MYCOMMON()
COMMON // A
MYCOMMON = LOC(A)
RETURN
END

This is the output of the previous C or C++ source code:

a =  1.0
b = 2.0   3.0   4.0   5.0   6.0   7.0   8.0   9.0  10.0  11.0

This is the output of the previous Fortran source code:

STA = 1.
STB = 2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.,  10.,  11.

13.3.7. Cray C and Fortran Example

Here is an example of a Cray C function that calls a Fortran subprogram. The Fortran subprogram example follows the Cray C function example, and the input and output from this sequence follows the Fortran subprogram example.

Note: This example assumes that the Cray Fortran function is compiled with the -s default32 option enabled. The examples will not work if the -s default64 option is enabled.

/*                 C program (main.c):                   */

#include <stdio.h>
#include <string.h>
#include <fortran.h>

/* Declare prototype of the Fortran function. Note the last */
/* argument passes the length of the first argument. */
fortran double FTNFCTN (char *, int *, int);

double FLOAT1 = 1.6;
double FLOAT2;  /* Initialized in FTNFCTN */

main()
{
     int clogical, ftnlogical, cstringlen;
     double rtnval;
     char *cstring = "C Character String";


/* Convert clogical to its Fortran equivalent */
     clogical = 1;
     ftnlogical = _btol(clogical);

/* Print values of variables before call to Fortran function */
     printf(" In main: FLOAT1 = %g; FLOAT2 = %g\n",
          FLOAT1, FLOAT2);
     printf(" Calling FTNFCTN with arguments:\n");
     printf(" string = \"%s\"; logical = %d\n\n", cstring, clogical);
     cstringlen = strlen(cstring);
     rtnval = FTNFCTN(cstring, &ftnlogical, cstringlen);

/* Convert ftnlogical to its C equivalent */
     clogical = _ltob(&ftnlogical);

/* Print values of variables after call to Fortran function */
     printf(" Back in main: FTNFCTN returned %g\n", rtnval);
     printf(" and changed the two arguments:\n");
     printf(" string = \"%.*s\"; logical = %d\n",
     cstringlen, cstring, clogical);
}

C                  Fortran subprogram (ftnfctn.f):


     FUNCTION FTNFCTN(STR, LOG)

     REAL FTNFCTN
     CHARACTER*(*) STR
     LOGICAL LOG

     COMMON /FLOAT1/FLOAT1
     COMMON /FLOAT2/FLOAT2
     REAL FLOAT1, FLOAT2
     DATA FLOAT2/2.4/          ! FLOAT1 INITIALIZED IN MAIN

C      PRINT CURRENT STATE OF VARIABLES
       PRINT*, '     IN FTNFCTN: FLOAT1 = ', FLOAT1,
      1                          ';FLOAT2 = ', FLOAT2
       PRINT*, '     ARGUMENTS:    STR = "', STR, '"; LOG = ', LOG

C      CHANGE THE VALUES FOR STR(ING) AND LOG(ICAL)
       STR = 'New Fortran String'
       LOG = .FALSE.

       FTNFCTN = 123.4
       PRINT*, '    RETURNING FROM FTNFCTN WITH ', FTNFCTN
       PRINT*
       RETURN
       END

The previous Cray C function and Fortran subprogram are executed by the following commands and produce the following output:

% cc -c main.c
% ftn -c ftnfctn.f
% ftn main.o ftnfctn.o
% ./a.out
In main: FLOAT1 = 1.6;  FLOAT2 = 2.4
Calling FTNFCTN with arguments:
string = "C Character String"; logical = 1

IN FTNFCTN: FLOAT1 = 1.6; FLOAT2 = 2.4
ARGUMENTS:   STR = "C Character String"; LOG = T
RETURNING FROM FTNFCTN WITH 123.4

Back in main: FTNFCTN returned 123.4
and changed the two arguments:
string = "New Fortran String"; logical = 0

13.3.8. Calling a Fortran Program from a Cray C++ Program

The following example illustrates how a Fortran program can be called from a Cray C++ program:

#include <iostream.h>
extern "C" int FORTRAN_ADD_INTS(int *arg1, int &arg2);

main() 
{
    int num1, num2, res;
    cout << "Start C++ main" << endl << endl;

    //Call FORTRAN function to add two integers and return result.
    //Note that the second argument is a reference parameter so
    //it is not necessary to take the address of the
    //variable num2.

    num1 = 10;
    num2 = 20;
    cout << "Before Call to FORTRAN_ADD_INTS" << endl;
    res = FORTRAN_ADD_INTS(&num1, num2);
    cout << "Result of FORTRAN Add = " << res << endl << endl;
    cout << "End C++ main" << endl;
}

The Fortran program that is called from the Cray C++ main function in the preceding example is as follows:

INTEGER FUNCTION FORTRAN_ADD_INTS(Arg1, Arg2)
INTEGER Arg1, Arg2

PRINT *," FORTRAN_ADD_INTS, Arg1,Arg2 = ", Arg1, Arg2
FORTRAN_ADD_INTS = Arg1 + Arg2
END

The output from the execution of the preceding example is as follows:

Start C++ main

Before Call to FORTRAN_ADD_INTS
  FORTRAN_ADD_INTS, Arg1,Arg2 =  10,  20
Result of FORTRAN Add = 30

End C++ main