NERSC logo National Energy Research Scientific Computing Center
  A DOE Office of Science User Facility
  at Lawrence Berkeley National Laboratory

Mixing C and Fortran on the SP

Routines written in Fortran and C/C++ can be mixed in a single program. The xlf compiling system assumes that C/C++ routine names are all lower case. Following this convention will make compiling and linking easier.

Care must be taken with data types that are passed to the subroutines. Here is a table showing corresponding datatypes.

FORTRAN C/C++Bytes
REAL,REAL*4,REAL(KIND=4) float 4
REAL*8,REAL(KIND=8),DOUBLE PRECISION double, long double 8
REAL*16,REAL(KIND=16) long double (with -qlongdouble or -qldbl128) 16
INTEGER, INTEGER*4, INTEGER(KIND=4) int, long int 4
INTEGER*8, INTEGER(KIND=8) long long int 8
INTEGER*2 short int 2
COMPLEX, COMPLEX*4 structure of two floats 8
COMPLEX*8 structure of two doubles 16
DOUBLE COMPLEX, COMPLEX*16 structure of two long doubles 32
CHARACTER See Working with characters below. 

The subsections of this topic are

For further information beyond that found on this page, see The XL Fortran User's Guide, chapter 10, "Interlanguage Calls."

Calling C from Fortran

As long as you keep in mind how the variable types correspond to each other in Fortran and C as shown in the table above, calling a C routine from Fortran is straightforward as long as the name of the C routine is all lowercase.

Fortran passes addresses in subroutines calls, so the C routine should accept a pointer to the proper data type.

Below is a simple example. First, the Fortran main program:

!FILENAME: f_main.f
!
PROGRAM f_calls_c
        IMPLICIT NONE

        REAL :: p1_x, p1_y, p2_x, p2_y
        EXTERNAL calc_dist

        p1_x = 0.0
        p1_y = 0.0
        p2_x = 3.0
        p2_y = 4.0

        CALL calc_dist(p1_x,p1_y,p2_x,p2_y)

END PROGRAM f_calls_c

And the C routine:

/* FILENAME: calc_c.c
*/
#include <math.h>
void calc_dist(float *x1, float *y1, float *x2, float *y2)
{
        float dxsq, dysq, distance;

        dxsq = (*x2-*x1)*(*x2-*x1);
        dysq = (*y2-*y1)*(*y2-*y1);

        distance = sqrt( dxsq + dysq );

        printf("The distance between the points is %13.5e\n",
			distance);

}

Compiling and executing:

%  cc -c calc_c.c 
%  xlf90  -o f_calls_c f_main.f calc_c.o 
** f_calls_c   === End of Compilation 1 ===
1501-510  Compilation successful for file f_main.f.
%  ./f_calls_c 
The distance between the points is   5.00000e+00

Calling Fortran from C

Calling a Fortran routine from C is similar. Here are the same two routines as shown above, but with the languages switched for each file.

/* FILENAME: c_main.c
   Shows how to call a FORTRAN routine from C
*/

extern void calc_dist(float*, float*, float*, float*);

void main()
{
        float p1_x,p1_y,p2_x,p2_y;

        p1_x = 0.0;
        p1_y = 0.0;
        p2_x = 3.0;
        p2_y = 4.0;

        calc_dist(&p1_x,&p1_y,&p2_x,&p2_y);
}

! FILENAME: calc_f.f
!
SUBROUTINE calc_dist(x1,y1,x2,y2)
        IMPLICIT NONE
        REAL :: x1,y1,x2,y2
        REAL :: distance

        distance = SQRT( (x2-x1)**2 + (y2-y1)**2 )

        WRITE(6,1) distance
1       FORMAT("The distance between the points is ", 1pe13.5)

END SUBROUTINE calc_dist

%  xlf90 -c calc_f.f 
** calc_dist   === End of Compilation 1 ===
1501-510  Compilation successful for file calc_f.f.
%  cc -o c_calls_f c_main.c calc_f.o -lxlf90 -lm
%  ./c_calls_f 
The distance between the points is   5.00000E+00

Note that we had to explicitly link with -lxlf90 -lm when using cc.

Mixing Fortran and C++

In order to mix Fortran and all C++ functions, you must pass the interlanguage calls through C "wrapper" functions. This is because the C++ compiler "mangles" the names of some C++ functions. C++ functions that are declared and used like regular C functions can be called just like C functions, however.

In addition, you must use the xlC command to perform the final linkage step.

Here's C++ code that performs the same task as does the C function above that calculates the distance between points.

/* FILENAME: calc_c++.h 
	This is the C++ routine.
*/
 #include <iostream.h>
 #include <math.h>

template<class T> class calc {

public:

calc() {cout <<"Inside C++ constructor"<<endl;}
~calc() {cout <<"Inside C++ destructor"<<endl;}
 
 void calc_dist(T *x1, T *y1, T *x2, T *y2)
 {
         T dxsq, dysq, distance;

         dxsq = (*x2-*x1)*(*x2-*x1);
         dysq = (*y2-*y1)*(*y2-*y1);

         distance = sqrt( dxsq + dysq );

         cout <<"The distance between the points is "
		<<distance<<" \n"<<endl;

 }
};

In order to call this routine safely from Fortran, you must "wrap" the function with a C-style function, something like this:

/* Filename: calc_cwrapper.C */
#include <stdio.h>
#include "calc_c++.h"

extern "C" void calc_dist(float *x,float *y,float *X,float *Y);

void calc_dist(float *x,float *y,float *X,float *Y) {
        printf("Inside C function, creating C++ object\n");

        calc<float>* c=new calc<float>();
        c->calc_dist(x,y,X,Y);
        
        printf("Back in C function, will delete C++ object\n");
        delete c;
}

Compiling and running (using f_main.f):

%  xlf90 -c f_main.f  
** f_calls_c   === End of Compilation 1 ===
1501-510  Compilation successful for file f_main.f.
%  xlC -c calc_cwrapper.C
% xlC -o f_calls_c++ calc_cwrapper.o f_main.o -lm -lxlf90
% ./f_calls_c++
Inside C function, creating C++ object
Inside C++ constructor
The distance between the points is 5 

Back in C function, will delete C++ object
Inside C++ destructor

Calling Fortran from C++

You can call Fortran subroutines from C++ by declaring the routine

	extern "C" void fortran_routine_name
or for Fortran functions
	extern "C" return_type fortran_function_name

Calling routines in Fortran modules

To call routines from C that are defined in Fortran 90 modules, you must refer to the Fortran ROUTINE as __modulename_NMOD_routine.

For example, the Fortran routine:

! FILENAME: calc_mod_f.f
!
MODULE CALCULATION

      CONTAINS
        SUBROUTINE CALC_DIST(x1,y1,x2,y2)
        IMPLICIT NONE
        REAL :: x1,y1,x2,y2
        REAL :: distance

        distance = SQRT( (x2-x1)**2 + (y2-y1)**2 ) 

        WRITE(6,1) distance
1       FORMAT("The distance between the points is ", 1pe13.5) 

        END SUBROUTINE CALC_DIST 

END MODULE CALCULATION

is called from the main C program in this way:

 
/* FILENAME: c_main_mod.c
   Shows how to call a FORTRAN routine from C
   In this case the FORTRAN routine, named CALC_DIST,  
        is in the FORTRAN module named calculation
*/

extern void __calculation_NMOD_calc_dist(float *, float *, 
				float *, float *);

void main() 
{
        float p1_x,p1_y,p2_x,p2_y;

        p1_x = 0.0;
        p1_y = 0.0;
        p2_x = 3.0;
        p2_y = 4.0;

        __calculation_NMOD_calc_dist(&p1_x,&p1_y,&p2_x,&p2_y);
}

Compiling and running:

%  xlf90 -c calc_mod_f.f 
** calculation   === End of Compilation 1 ===
1501-510  Compilation successful for file calc_mod_f.f.
% cc -o c_calls_f_mod c_main_mod.c calc_mod_f.o -lxlf90 -lm 
% ./c_calls_f_mod
The distance between the points is   5.00000E+00

Working with characters

Passing characters and strings between Fortran and C is not straightforward. When Fortran passes a CHARACTER string, it passes (by value) an extra argument that contains the length of the string. In addition, C strings are usually null-terminated, while Fortran character strings are not.

To pass a Fortran CHARACTER to a C routine that accepts a char, the string must be null terminated.

The only character type in Fortran is CHARACTER, which is stored as a set of contiguous bytes, one character per byte. The length is not stored as part of the entity. Instead, it is passed by value as an extra argument at the end of the declared argument list when the entity is passed as an argument.

If you are writing both parts of the mixed-language program, you can make the C routines deal with the extra Fortran length argument, or you can suppress this extra argument by passing the string using the %REF function. If you use %REF, which you typically would for pre-existing C routines, you need to indicate where the string ends by concatenating a null character to the end of each character string that is passed to a C routine.

Following is an example that shows some correct and incorrect ways to pass and handle characters and strings from Fortran to C.

!FILENAME: give_chars.f
!
! Gives some examples of passing Fortran strings to
! a C routine that prints a character string beginning
! at the address of the start of the passed character
! string and ending at the C string termination character, which is
! CHAR(0) in Fortran.
 
PROGRAM give_chars

        IMPLICIT NONE

        CHARACTER(LEN=1)::  a(9),d
        CHARACTER(LEN=8)::  b,c,e
        REAL:: f
        EXTERNAL TAKE_CHARS 
        
! These are put in COMMON block to insure they are all 
! adjacent in memory

        COMMON//b,c,d
        COMMON/c2/e,f

        a(1) = 'a'
        a(2) = 'b'
        a(3) = 'c'
        a(4) = 'd'
        a(5) = 'e'
        a(6) = 'f' 
        a(7) = 'g'
        a(8) = 'h'

! This behavior of this call is undefined, since there is
! no string terminator character being passed to the C
! routine. It will 'work' if there fortuitously happens to be
! a zero in the next memory location after a(8).
 
        PRINT *, ""
        PRINT *, "This may or may not print a(1) to a(8):"
        CALL TAKE_CHARS(a)


! This puts a string terminator character in a(6)

        a(6) = CHAR(0)  ! String terminator for C routine
        PRINT *, ""
        PRINT *, "This will print a(1) to a(5):"
        CALL TAKE_CHARS(a)

!
        b='ABCDEFGH'
        c='ijklmnop'
        d = CHAR(0)     !String terminator for C routine

!       The string terminator in d follows the string c in memory
!       but there is no string terminator character between c and d
!       (All this controlled by COMMON block declaration).

        PRINT *, ""
        PRINT *, "This will print string b and c:"
        CALL TAKE_CHARS(b)

!       Here we append a string terminator character to  the
!       string b when we pass it. 

        PRINT *, ""
        PRINT *, "This will print string b only:"
        CALL TAKE_CHARS(b // CHAR(0)) !Appends string terminator to b

!       The following will print string e plus garbage
!       because the C routine does not see a string terminator
!       before the REAL value f 

        e = 'abcdefgh'
        f = ATAN(1.)
        PRINT *, ""
        PRINT *, "This will print string e plus garbage:"
        CALL TAKE_CHARS(e)
         
                
END PROGRAM give_chars

 
/* FILENAME: take_chars.c
   A routine to take a Fortran character string
   and print it. It assumes that the Fortran routine
   has explicitly appended a string terminator character
   to the string before passing it.
*/


void take_chars(char *fstring, long int fstringLen)
{
        
/*      This printf() function (with %s) will print characters until
        it encounters a \0 character, which it interprets as an 
        end-of-string signal.
*/ 
         
        printf("The passed string is: %s\n",fstring);
        printf("The passed string length is: %d\n",fstringLen);
}

Compile these routines with

%  xlf90 -c give_chars.f
%  cc -c take_chars.c  
%  xlf90 -o give_chars give_chars.o take_chars.o  

When we run the program, we get:

%  ./give_chars 
This may or may not print a(1) to a(8):
The passed string is: abcdefgh
The passed string length is: 1
 
 This will print a(1) to a(5):
The passed string is: abcde
The passed string length is: 1
 
 This will print string b and c:
The passed string is: ABCDEFGHijklmnop
The passed string length is: 8
 
 This will print string b only:
The passed string is: ABCDEFGH
The passed string length is: 9
 
 This will print string e plus garbage:
The passed string is: abcdefgh?IÛ
The passed string length is: 8

Using mixed case names

Sometimes you may want to link with code containing C/C++ routines that have mixed-case or upper-case names. You can use these routines by using the -U option when compiling with xlf. However, this makes all functions, variables, and names case-sensitive.

You can also use the @PROCESS MIXED directive to declare case sensitivity. For example:

@process mixed
	external C_Func	   ! now we can call C_Func, not just c_func
	integer aBc, ABC   ! now these are two different variables
	common /xYz/ aBc   ! the same applies to common block names -
	common /XYZ/ ABC   ! these two are different

	end

LBNL Home
Page last modified: Fri, 29 Feb 2008 23:38:13 GMT
Page URL: http://www.nersc.gov/nusers/resources/software/ibm/c_and_f.php
Web contact: webmaster@nersc.gov
Computing questions: consult@nersc.gov

Privacy and Security Notice
DOE Office of Science