For the case of Fortran, most compiler vendors, including the Portland Group, have adopted a convention first introduced by Sun Microsystems, wherein Fortran routine names are translated to external names by appending a single underscore (_) to the source code name. A notable exception in this instance are the GNU Fortran compilers, which by default (apparently for sheer perversity), append a single underscore to a Fortran routine name, unless said name already contains an underscore, in which case two underscores are appended! Fortunately, there is an option to the f77, namely -fno-second-underscore, that instructs the compiler to act in a sane and conventional fashion, and use of this switch is recommended practice.
#include <stdio.h> /*========================================================== Simple example to illustrate fact that scalar parameters are passed BY VALUE in C. Calling function 'c_fcn' has NO side effect vis a vis the value of 'i' in the main routine. ==========================================================*/ void c_fcn(int ival); int main(int argc, char ** argv) { int i; i = 10; printf("c_call: In main before call to c_fcn, i = %d\n\n",i); (void) c_fcn(i); printf("c_call: In main after call to c_fcn, i = %d\n",i); } void c_fcn(int ival) { printf("c_fcn: Entering routine, ival = %d\n",ival); ival = 0; printf("c_fcn: Exiting routine, ival = %d\n\n",ival); }With an appropriate Makefile, we can build the corresponding executable in the usual fashion:
% make c-call pgcc -g -c c-call.c pgcc -g -L/usr/local/PGI/lib c-call.o -o c-callExecution of the program then produces output as follows:
% c_call c_call: In main before call to c_fcn, i = 10 c_fcn: Entering routine, ival = 10 c_fcn: Exiting routine, ival = 0 c_call: In main after call to c_fcn, i = 10The key thing to observe here is that the assignment statement
ival = 0;in the function c_fcn has no effect on the value of the corresponding actual argument, i, in the main program. In effect, when the function is called from the main program, the function allocates new storage for the variable ival and initializes that storage by copying the value of i from the main program.
Contrast the above behaviour with that of the analogous Fortran program:
c=========================================================== c f77_call: c c Simple example to illustrate fact that ALL routine c parameters, including scalars, are passed BY c ADDRESS in f77. Calling subroutine 'f77_sub' has c side-effect of setting the value of 'i' in the c main routine to 0. c=========================================================== program f77_call implicit none integer i i = 10 write(*,*) 'f77_call: In main before call to f77_sub,', & ' i = ', i write(*,*) call f77_sub(i) write(*,*) 'f77_call: In main after call to f77_sub,', & ' i = ', i stop end subroutine f77_sub(ival) implicit none integer ival write(*,*) 'f77_sub: Entering routine, ival = ', ival ival = 0 write(*,*) 'f77_sub: Exiting routine, ival = ', ival write(*,*) return endAgain, creation of the corresponding executable is straightforward:
% make f77-call pgf77 -g -c f77-call.f pgf77 -g -L/usr/local/PGI/lib f77-call.o -o f77-calland execution of f77-call produces the following output:
% f77-call f77_call: In main before call to f77_sub, i = 10 f77_sub: Entering routine, ival = 10 f77_sub: Exiting routine, ival = 0 f77_call: In main after call to f77_sub, i = 0In this case the assignment statement
ival = 0in the f77_sub subroutine does change the value of the variable, i, that is supplied as an argument to f77_sub in the main program. Indeed, because of the call-by-address mechanism, i in the main program and ival in the subroutine occupy, during the execution of the subroutine, the same storage.
c=========================================================== c Definitions of f77 routines that are to be called from c C. c=========================================================== integer function f77_int_fcn(isc, iarray, liarray) implicit none integer isc, liarray, iarray(liarray) integer i write(*,*) 'f77_int_fcn: Entering routine' write(*,*) ' isc = ', isc do i = 1 , liarray write(*,*) ' iarray(', i, ') = ', iarray(i) end do write(*,*) 'f77_int_fcn: Exiting routine' write(*,*) f77_int_fcn = -1 return end real*8 function f77_real8_fcn(rsc, rarray, lrarray) implicit none integer lrarray real*8 rsc, rarray(lrarray) integer i write(*,*) 'f77_real8_fcn: Entering routine' write(*,*) ' rsc = ', rsc do i = 1 , lrarray write(*,*) ' rarray(', i, ') = ', rarray(i) end do write(*,*) 'f77_real8_fcn: Exiting routine' write(*,*) f77_real8_fcn = -1.0d0 return end subroutine f77_sub(isc, rsc) implicit none integer isc real*8 rsc write(*,*) 'f77_sub: Entering routine' write(*,*) ' isc = ', isc write(*,*) ' rsc = ', rsc write(*,*) 'f77_sub: Exiting routine' write(*,*) return endand here is the C main routine, contained in a source file, c-calls-f77.c, that calls the Fortran functions and subroutine:
#include <stdio.h> /*========================================================== c_calls_f77 Illustrates invocation of f77 routines from C program assuming that the compiler system uses "append single underscore" conventiopn for translating f77 names to external symbols. f77 routines are defined in source file 'f77-routines.f' ==========================================================*/ /*--------------------------------------------------------- Declarations of f77 routines. ---------------------------------------------------------*/ int f77_int_fcn_(int * pisc, int * iarray, int * pliarray); double f77_real8_fcn_(double * prsc, double * rarray, int * plrarray); void f77_sub_(int * pisc, double * prsc); int main(int argc, char ** argv) { /*--------------------------------------------------------- Variables to be passed to f77 routines. ---------------------------------------------------------*/ int isc, liarray, lrarray, iarray[10]; double rsc, rarray[10]; /*--------------------------------------------------------- Miscellaneous variables. ---------------------------------------------------------*/ int i, ival; double rval; isc = 0; rsc = 0; liarray = 10; lrarray = 10; for( i = 0; i < liarray; i++ ) { iarray[i] = i; rarray[i] = i; } /*--------------------------------------------------------- Invoke f77 routines. Note that ADDRESSES of scalar vbls must be passed for those parameters where the f77 routine expects a scalar. ---------------------------------------------------------*/ ival = f77_int_fcn_(&isc, iarray, &liarray); rval = f77_real8_fcn_(&rsc, rarray, &liarray); (void) f77_sub_(&isc, &rsc); printf("f77_int_fcn(...) returns %d\n",ival); printf("f77_real8_fcn(...) returns %g\n",rval); exit(0); }Note that the declarations (prototypes) of the Fortran functions and subroutines must precede the invocations of these routines by the C-code, and that the Fortran routines must be declared and invoked with a trailing underscore, per the discussion above. Also note how the addresses of scalar variables such as isc and rsc must be supplied to the Fortran routines via the "address-of" operator, &.
To build the corresponding executable, each of the two source files, c-calls-f77.c and f77-routines.f, must be compiled with the appropriate compiler, using the -c option in each case to produce object code. The two object files, c-calls-f77.o and f77-routines.o are then linked together using the C compiler, which in turn invokes the loader program, ld.
Crucially, in the link phase, additional libraries which provide the basic run time support for Fortran programs must be supplied to the loader. Which specific libraries must be specified will always depend on which compiler suite is being used, and often on which specific version of a given suite is employed. One must often scour manuals and user guides to determine the correct libraries, although in the current epoch, the needed information can also frequently be found via on-line searches.
For the version of the PG compilers currently being used in this course, only a single library, libpgftnftl.a, must be included to provide the Fortran run time support, so we only need to ensure that
-lpgftnrtlis included in the link command.
The executable for our C-calls-Fortran example can thus be generated as follows
% make c-calls-f77 pgcc -g -c c-calls-f77.c pgf77 -g -c f77-routines.f pgcc -g -L/usr/local/PGI/lib c-calls-f77.o f77-routines.o -lpgftnrtl \ -o c-calls-f77Note that if we had omitted -lpgftnrtl, in the load phase, the linker would have complained vociferously and voluminously:
% pgcc -g -L/usr/local/PGI/lib c-calls-f77.o f77-routines.o -o c-calls-f77 f77-routines.o(.text+0x3c): In function `f77_int_fcn': f77-routines.f:12: undefined reference to `fio_src_info' f77-routines.o(.text+0x55):f77-routines.f:12: undefined reference to `fio_ldw_init' f77-routines.o(.text+0x83):f77-routines.f:12: undefined reference to `fio_ldw' . . . f77-routines.o(.text+0x2e3):f77-routines.f:18: undefined reference to `fio_ldw_end' f77-routines.o(.text+0x32c): In function `f77_real8_fcn': f77-routines.f:33: undefined reference to `fio_src_info' f77-routines.o(.text+0x345):f77-routines.f:33: undefined reference to `fio_ldw_init' f77-routines.o(.text+0x373):f77-routines.f:33: undefined reference to `fio_ldw' . . . f77-routines.o(.text+0x5d3):f77-routines.f:39: undefined reference to `fio_ldw_end' f77-routines.o(.text+0x611): In function `f77_sub': f77-routines.f:53: undefined reference to `fio_src_info' f77-routines.o(.text+0x62a):f77-routines.f:53: undefined reference to `fio_ldw_init' f77-routines.o(.text+0x658):f77-routines.f:53: undefined reference to `fio_ldw' . . . f77-routines.o(.text+0x825):f77-routines.f:57: undefined reference to `fio_ldw_end'The execution of c-calls-f77 now produces the expected output:
% c-calls-f77 f77_int_fcn: Entering routine isc = 0 iarray( 1) = 0 iarray( 2) = 1 iarray( 3) = 2 iarray( 4) = 3 iarray( 5) = 4 iarray( 6) = 5 iarray( 7) = 6 iarray( 8) = 7 iarray( 9) = 8 iarray( 10) = 9 f77_int_fcn: Exiting routine f77_real8_fcn: Entering routine rsc = 0.0000000000000000E+000 rarray( 1) = 0.0000000000000000E+000 rarray( 2) = 1.000000000000000 rarray( 3) = 2.000000000000000 rarray( 4) = 3.000000000000000 rarray( 5) = 4.000000000000000 rarray( 6) = 5.000000000000000 rarray( 7) = 6.000000000000000 rarray( 8) = 7.000000000000000 rarray( 9) = 8.000000000000000 rarray( 10) = 9.000000000000000 f77_real8_fcn: Exiting routine f77_sub: Entering routine isc = 0 rsc = 0.0000000000000000E+000 f77_sub: Exiting routine f77_int_fcn(...) returns -1 f77_real8_fcn(...) returns -1The above code can be used as template/model for any situation likely to be encountered in this course wherein a C-programmer wishes to call one or more Fortran routines from his/her C code.
Also, for convenience, here are the required Fortran run-time-support libraries currently needed for various compiler suites:
Thus, in order to call a C routine having Fortran-compatible arguments, but that has a name that does not end in an underscore, we must add an additional layer of C code, which defines an "interface" routine, with a name that does end in an underscore, and which then invokes the desired C "base" routine.
The source code file c-routines.c defines both "interface" and "base" routines completely analogous to those defined previously in f-routines.f:
#include <stdio.h> #include "c-routines.h" /*============================================================= Definitions of interface routines that can be invoked DIRECTLY from Fortran. Note that since all arguments in f77 are passed BY ADDRESS, arguments that are scalar in the f77 call must be defined as pointer-to-appropriate-type in the code for the C function. =============================================================*/ int c_int_fcn_(int * pisc, int * iarray, int * pliarray) { return c_int_fcn(*pisc, iarray, *pliarray); } double c_double_fcn_(double * prsc, double * rarray, int * plrarray) { return c_double_fcn(*prsc, rarray, *plrarray); } void c_void_fcn_(int * pisc,double * prsc) { (void) c_void_fcn(*pisc, *prsc); return; } /*============================================================= Definitions of C routines that are to be called (indirectly) from Fortran =============================================================*/ int c_int_fcn(int isc, int * iarray, int liarray) { int i; printf("c_int_fcn: Entering routine ... \n"); printf(" isc = %d\n",isc); printf(" liarray = %d\n",liarray); for( i = 0; i < liarray; i++) { printf("iarray[%d] = %d\n", i, iarray[i]); } printf("c_int_fcn: Exiting routine ... \n\n"); return -1; } double c_double_fcn(double rsc, double * rarray, int lrarray) { int i; printf("c_double_fcn: Entering routine ... \n"); printf(" rsc = %d\n",rsc); printf(" liarray = %d\n",lrarray); for( i = 0; i < lrarray; i++) { printf("rarray[%d] = %g\n", i, rarray[i]); } printf("c_double_fcn: Exiting routine ... \n\n"); return -1.0; } void c_void_fcn(int isc,double rsc) { printf("c_void_fcn: Entering routine ... \n"); printf(" isc = %d\n",isc); printf(" rsc = %d\n",rsc); printf("c_void_fcn: Exiting routine ... \n\n"); }Observe that there is one interface function for each base function, and that the interface function simply returns the invocation of the base function with scalar arguments dereferenced using the dereferencing operator, *, as necessary.
The Fortran main program, f77-calls-c.f that invokes the C functions is as follows
c=========================================================== c f77_calls_c: c c Illustrates invocation of C routines from f77 program c assuming that the compiler system uses "append single c underscore" convention for translating f77 names to c external symbols. c c C routines are defined in source file 'c-routines.c'. c=========================================================== program f77_calls_c implicit none c----------------------------------------------------------- c Declaration of C-callable functions, no declarations c are necessary/possible for 'void' functions. c C routines per se must have '_' appended to the c function name in definition (see 'c-routines.c'). c----------------------------------------------------------- integer c_int_fcn real*8 c_double_fcn c----------------------------------------------------------- c Variables to be passed to C routines. c----------------------------------------------------------- integer isc, liarray, lrarray, iarray(10) real*8 rsc, rarray(10) c----------------------------------------------------------- c Miscellaneous variables. c----------------------------------------------------------- integer i, ival real*8 rval isc = 0 rsc = 0 liarray = 10 lrarray = 10 do i = 1 , 10 iarray(i) = i rarray(i) = i end do c----------------------------------------------------------- c Corresponding C routines must have '_' appended to c function name: see 'c-routines.c' c----------------------------------------------------------- ival = c_int_fcn(isc,iarray,liarray) rval = c_double_fcn(rsc,rarray,lrarray) call c_void_fcn(isc,rsc) write(*,*) 'c_int_fcn(...) returns', ival write(*,*) 'c_double_fcn(...) returns', rval stop endOnce again, in order to create the executable f77-calls-c, the two source files, f77-calls-c.f and c-routines.c, must be separately compiled with the Fortran and C compilers respectively, using the -c option to produce object code. However, in this case, when linking the resulting object files together to produce an executable with the Fortran compiler, there is no need to specify additional libraries, unless those libraries would be needed by the C routines themselves.
The executable is thus created as follows:
% make f77-calls-c pgf77 -g -c f77-calls-c.f pgcc -g -c c-routines.c pgf77 -g -L/usr/local/PGI/lib f77-calls-c.o c-routines.o -o f77-calls-cand, again, execution of f77-calls-c produces the expected output:
% f77-calls-c c_int_fcn: Entering routine ... isc = 0 liarray = 10 iarray[0] = 1 iarray[1] = 2 iarray[2] = 3 iarray[3] = 4 iarray[4] = 5 iarray[5] = 6 iarray[6] = 7 iarray[7] = 8 iarray[8] = 9 iarray[9] = 10 c_int_fcn: Exiting routine ... c_double_fcn: Entering routine ... rsc = 0 liarray = 10 rarray[0] = 1 rarray[1] = 2 rarray[2] = 3 rarray[3] = 4 rarray[4] = 5 rarray[5] = 6 rarray[6] = 7 rarray[7] = 8 rarray[8] = 9 rarray[9] = 10 c_double_fcn: Exiting routine ... c_void_fcn: Entering routine ... isc = 0 rsc = 0 c_void_fcn: Exiting routine ... c_int_fcn(...) returns -1 c_double_fcn(...) returns -1.000000000000000