SoftwareCompilersLibraries Applications Tools & Utilities Software by PlatformFranklinBassi Jacquard DaVinci PDSF HPSS Affiliated CollectionsACTS Collection |
Mixing C and Fortran on the SPRoutines 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.
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 FortranAs 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 CCalling 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_nameor 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 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 charactersPassing 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 namesSometimes 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 |
![]() |
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 |
![]() |