Brief Notes on Mixed Language Programming - FORTRAN and C ----------------------------------------- ------------- Glenn Geers (glenn@physics.su.oz.au) Last updated: Sun Sep 11 18:29:28 EST 1994 This brief note describes some of my experiences with mixed language programming on UNIX machines. An attempt is made to indicate where difficulties in porting to different operating systems may occur. One deficiency, at present, is the absence of a discussion of the FORTRAN CHARACTER type. This will be rectified in a subsequent version. Subprogram Calling Conventions ------------------------------ Under UNIX, f77 uses the same calling convention as used by C. Specifically, for each subprogram call a stack frame is established when the procedure is entered and destroyed when the procedure is exited. This is really not of much concern unless you intend linking in home grown assembly language routines. Under some OS's the FORTRAN and C calling conventions are different. The only thing you can be sure of is that C must establish stack frames since C functions (even main()) may be recursive. If you have any doubt about the calling conventions use f2c. It should also be noted that under f77 on UNIX boxes ANY FORTRAN routine may also be recursive but watch out; your code may not be portable to other OS's. External Names -------------- Under UNIX FORTRAN subroutine and function names have an underscore appended to them (but not under AIX) to avoid clashes with names occurring in libraries and with language constructs - FORTRAN has no reserved words. For example: subroutine foo() is visible from a C program as: foo_() In order to facilitate porting to AIX it is best to have defines such as: #if !defined(aix) #define FOO_ foo #else #define FOO_ foo_ #endif You then put FOO_() in your C in all cases. This also allows you to get around the FORTRAN 77 limit of 6 character function and subroutine names in the C code. On CRAYs FORTRAN external names are all upper case and don't have the underscore appended: #if defined(CRAY) #define FOO_ FOO #endif Note that you have to explicitly define aix since the C compiler under AIX conforms to the ANSI standard - no preprocessor defines are inherently available. Also note that the FORTRAN compiler under AIX (xlf) does provide an option, -qextname, that appends an underscore to user defined external names. When I initially wrote this document I was not aware of this facility. Subprogram Arguments -------------------- FORTRAN always expects its arguments to be passed by reference. In short this means that a subprogram is free to change the value of any of its arguments. This requirement gives rise to the now infamous case of FORTRAN routines changing the value of constants. It should be pointed out that programs that attempt to change the value of subroutine (or function) arguments are not FORTRAN 77 standard conforming and so the compiler is free to do whatever it wants. The behaviour varies from CHANGING THE VALUE OF THE CONSTANT (seen in SunOS f77, f2c, Fujitsu frt, UNICOS cft77) to acting as if the constant had been PASSED BY VALUE (AIX xlf). To test your compiler try running the following short program (NOT STANDARD CONFORMING). program testit C PROGRAM TO TEST THE BEHAVIOUR OF A COMPILER WHEN A SUBROUTINE C CHANGES THE VALUE OF ITS ARGUMENTS. C C UNDER SYSTEMS THAT ALLOW CONSTANTS TO CHANGE VALUE THE OUTPUT C WILL BE: C C first arg 2 C second arg 3 C first arg 3 C second arg 3 C first arg 3 C second arg 3 C first arg 3 C second arg 3 C C Thanks to David Dawes (dawes@physics.su.oz.au) for pointing out this C behaviour under SunOS. fred(2,3) fred(2,3) end subroutine fred(i,j) integer i,j write(*,*) 'first arg', i write(*,*) 'second arg', j C CHANGE ARGUMENTS i=j write(*,*) 'first arg', i write(*,*) 'second arg', j return end The upshot of the passing by reference requirement is that all arguments that are passed from C to FORTRAN must be of pointer type. And any C function called from FORTRAN must be set up with arguments of pointer type. Some examples: subroutine glitch(a,b,c) integer a double precision b real c is called from C as: int x = 4; double *y; float z = 1.2e12; *y = 3.2; glitch_(&x,y,&z); and the C function int frob_(a,b) int *a; double *b; may be called from FORTRAN by: call frob(3, 2.7e2) Typing of FORTRAN Subroutines and Functions ------------------------------------------- A subroutine is typed as a C function returning an int. The value returned is dependent on the value of the alternate returns (if any) from the subroutine. A FORTRAN function (character valued functions are not discussed) returns the same type it is defined to return in FORTRAN with the exception of functions returning type complex or double complex. In these cases an additional argument must be provided when calling the FORTRAN function from C in order to provide space for the returned value. Arrays ------ FORTRAN arrays start at element number 1. For example: real bar(100) declares an array bar with elements numbered 1 to 100. The C declaration float bar[100] declares an array bar with elements numbered 0 to 99. Notice that the number of elements is the same - just the numbering has changed. The easiest way to get around this is to declare FORTRAN arrays to match C arrays. This is possible in FORTRAN 77 although not in the now deprecated FORTRAN 66. So the FORTRAN declaration becomes: real bar(0:99) This may cause problems when using canned FORTRAN routines where the default range is used. The solution in this case is to relabel the C array noting that bar[0] == bar(1). Such relabeling may be frustrating - it is often better to modify the canned routine (if source is available). You can also get around this by using pointer techniques. Note that FORTRAN arrays are stored in column major order and that C arrays are stored in row major idea. This means that the order of the indices must be switched. For example: a(2,3) == a[3][2]. Perhaps the simplest way around the array passing dilemma is to allocate C arrays (or dynamically allocated memory areas) with one more element than required, for example a 100 element real array in FORTRAN would be declared as a 101 element float array in C. You then use a #define to map element 1 of the C array to element 1 of the FORTRAN array. The following C code is provided as an example. #define PF(x) &x[1] float fred[101]; /* don't use fred[0] FORTRAN won't see it */ .... { bar_(PF(fred)); } Of course, this method only works for 1-dimensional arrays. Dynamic Allocation of 2-Dimensional Arrays ------------------------------------------ The following functions show how to create and free 2-dimensional arrays that can be passed between FORTRAN and C. They can be generalised to arbitrary types (including complex and double complex); #include #include #ifndef CRAY #define FOWRITE_ fowrit_ #else #define FOWRITE FOWRIT #endif double ** d2d(int size) { double *ptr; double **ret; int n = size*size; REGISTER int i,j; /* ** Allocate a block of memory of length size^2. ** Each memory location is of type double. ** In FORTRAN terms this is a one dimensional array 0:(size-1)*(size-1). ** This will form the columns of the 2-d array. ** Check for allocation failure. ** Note that calloc zeros the memory area. */ ptr = (double *)calloc(n,sizeof(double)); if (ptr == (double *)NULL) error("First stage memory allocation failure in c2d", EMEM); /* ** Allocate a block of memory of length size. ** Each memory location is of type double *. ** In FORTRAN terms this means that each location ** can hold an entire one dimensional array (of arbitrary length) ** of type double. ** malloc does not zero the memory area, but this is ok because ** these elements are assigned to below. */ ret = (double **)malloc(size*sizeof(double *)); if (ret == (double **)NULL) error("Second stage memory allocation failure in c2d", EMEM); /* ** The body of the loop assigns the address of the ** 0,size,2*size,...,size*size element of ptr to the 0,1,2,...,size ** element of ret. Thus forming a valid 2-d array each of whose elements ** is of type double. */ for (i=0,j=0; j<=size; i+=size,j++) ret[j] = &ptr[i]; return(ret); } void free_d2d(ptr, size) double **ptr; int size; { REGISTER int i,j; for (i=size,j=0; j int, REAL -> float and DOUBLE PRECISION -> double. On most UNIX (i.e. non-ANSI) C compilers REAL is promoted to DOUBLE PRECISION so some care is needed. Under ANSI C there is no problem. The FORTRAN function TYPE function barfoo(a,b) corresponds to the C function type barfoo_(a,b) float *a, *b; where type is the C type corresponding to TYPE. FORTRAN Functions Returning Non-scalar C Types ---------------------------------------------- The FORTRAN type COMPLEX (and UNIX f77 DOUBLE COMPLEX) exactly maps to the C stuct: struct {float r, i} complex; /* for DOUBLE COMPLEX replace float by double */ Consequently passing arrays of type COMPLEX between C and FORTRAN is as easy as for other types. Functions are another matter. The FORTRAN function complex function foobar(a,b) maps to the function int foobar_(ret, a, b) complex *ret; float *a,*b; where an appropriate typedef of the complex type has been made. The additional argument provides space for the function return value. UNIX f77 places return values as the first argument on the function call stack. The reason for this is probably that the function return type is known before the number and type of the functions' arguments are. The MIPS manual that I have (dated 15 February 1987) states that "You cannot return complex types between C and FORTRAN". In general, complex valued functions should be replaced by subroutines whose first argument is the value to be returned. This mechanism will work on all UNIX systems. A Note on Libraries ------------------- On most UNIX systems the FORTRAN I/O library is built on top of the C stdio package so it is possible to force flushing of the standard output and standard error units by calling a C function such as: void flushs_() { fflush(stdout); fflush(stderr); } Miscellaneous ------------- On UNIX systems that support dynamic linking (e.g. SunOS, SysVr4) it is necessary to define the function MAIN__() as: int MAIN__() {return(0);} in your C code if the main program is not written in FORTRAN. This will not be a problem if the program is linked statically --- MAIN__() is purely a dummy function. On at least one other UNIX variant (Ultrix) it is necessary to define: int MAIN_() {return(0);} in you C code if the main program is not written in FORTRAN. Note that this is necessary even though Ultirx does not support dynamic linking. On Fujitsu VP's the main(argc, argv) routine in C cannot exist when linking C and FORTRAN. When using these machines main(argc, argv) is replaced by MAIN__(argc, argv).