C-Fortran Interface |
11 |
The discussion is inherently limited to the specifics of the Sun Fortran 77, Fortran 90, and C compilers.
Note - Material common to both Sun Fortran 77 and Fortran 90 is presented in examples that use Fortran 77.
void) value.
RETURN statement. If no expression appears on the RETURN statement, zero is returned.
int, int long, and long, are equivalent (4 bytes).
REAL*16 and COMPLEX*32 are only available on SPARC and PowerPC.
INTEGER, for KIND=1, 2, or 4, take 4 bytes, align on 4-byte boundaries, and use 32-bit arithmetic.
int, int long, and long, are equivalent (4 bytes).
There are two usual solutions to the uppercase/lowercase problem:
Most examples in this chapter use all lowercase letters for the name in the C function, and do not use the f77 -U compiler option. (f90 1.2 does not have an equivalent option.)
There are three usual solutions to the underscore problem:
The examples in this chapter could use the Fortran 77 C() compiler pragma to avoid underscores. The C() pragma directive takes the names of external functions as arguments. It specifies that these functions are written in the C language, so the Fortran compiler does not append an underscore as it ordinarily does with external names. The C()directive for a particular function must appear before the first reference to that function. It must also appear in each subprogram that contains such a reference. The conventional usage is:
EXTERNAL ABC, XYZ !$PRAGMA C( ABC, XYZ ) |
If you use this pragma, the C function does not need an underscore appended to the function name.
Argument-Passing by Reference or Value
In general, Fortran routines pass arguments by reference. In a call, if you enclose an argument with the f77 nonstandard function %VAL(), the calling routine passes it by value. Argument Order
Except for arguments that are character strings, Fortran and C pass arguments in the same order. However, for every argument of character type, the Fortran routine passes an additional argument giving the length of the string. These are long int quantities in C, passed by value.
|
This Fortran code fragment: |
Is equivalent to this in C: |
CHARACTER*7 S INTEGER B(3) ... CALL SAM( S, B(2) ) |
char s[7]; long b[3]; ... sam_( s, &b[1], 7L ) ; |
Array Indexing and Order
Array indexing and order differ between Fortran and C. Array Indexing
C arrays always start at zero, but by default Fortran arrays start at 1. There are two usual ways of approaching indexing.
B(0) as follows:
INTEGER B(0:2) |
A(3,2)
A(1,1) A(2,1) A(3,1) A(1,2) A(2,2) A(3,2) A(1,3) A(2,3) A(3,3)C arrays in row-major order:
A[3][2]
A[0][0] A[0][1] A[0][2] A[1][0] A[1][1] A[1][2] A[2][0] A[2][1] A[2][2]For one-dimensional arrays, this is no problem. For two-dimensional and higher arrays, be aware of how subscripts appear and are used in all references and declarations--some adjustments may be necessary.
For example, it may be confusing to do part of a matrix manipulation in C and the rest in Fortran. It may be preferable to pass an entire array to a routine in the other language and perform all the matrix manipulation in that routine to avoid doing part in C and part in Fortran.
Many C programs use a set of subroutines, called standard I/O (or stdio). Many functions of Fortran I/O use standard I/O, which in turn uses operating system I/O calls. Some of the characteristics of these I/O systems are listed in Table 11-3.
File Permissions
C programmers typically open input files for reading and output files for writing or for reading and writing. In Fortran, it is not possible for the system to foresee what use you will make of a file, since there is no parameter to the OPEN statement that gives that information. Libraries and Linking With the f77 or f90 Command
To link the proper Fortran and C libraries, use the f77 or f90 command to invoke the linker.
demo% cc -c RetCmplxmain.c demo% f77 RetCmplx.f RetCmplxmain.o $lt;-- This command line does the linking. demo% a.out 4.0 4.5 8.0 9.0 demo% |
Passing Data Arguments by Reference
The standard method for passing data between Fortran routines and C procedures is by reference. To a C procedure, a Fortran subroutine or function call looks like a procedure call with all arguments represented by pointers. The only peculiarity is the way Fortran handles character strings as arguments and as the return value from a CHARACTER*n function. Simple Data Types
COMPLEX or CHARACTER strings), define or pass each associated argument in the C routine as a pointer:
COMPLEX Data
COMPLEX data as a pointer to a C struct of two floats or two doubles:Code Example 11-2
Passing COMPLEX Data Types
|
complex w
double complex z
external CCmplx
call CCmplx(w,z)
...
------------------------------
struct cpx {float r, i;};
struct dpx {double r,i;};
void ccmplx_(
struct cpx *w,
struct dpx *z)
{
w -> r = 32.;
w -> i = .007;
z -> r = 66.67;
z -> i = 94.1;
}
struct cpx {float r, i;};
struct cpx d1;
struct cpx *w = &d1;
struct dpx {double r, i;};
struct dpx d2;
struct dpx *z = &d2;
fcmplx_( w, z );
...
---------------
subroutine FCmplx( w, z )
complex w
double complex z
w = (32., .007)
z = (66.67, 94.1)
return
end
character type in the argument list. The extra argument gives the length of the string and is equivalent to a C long int passed by value. (This is implementation dependent.) The extra string-length arguments appears after the explicit arguments in the call.
| Code Example 11-3 Passing a CHARACTER string |
CHARACTER*7 S
INTEGER B(3)
...
CALL CSTRNG( S, B(2) )
...
char s[7];
long b[3];
...
cstrng_( s, &b[1], 7L );
...
If the length of the string is not needed in the called routine, the extra arguments may be ignored. However, note that Fortran does not automatically terminate strings with the explicit null character that C expects. This must be added by the calling program.
One-Dimensional Arrays
| Code Example 11-4 Passing a One-Dimensional Array |
integer i, Sum
integer a(9)
external FixVec
...
call FixVec ( a, Sum )
...
------------------------------
void fixvec_ (
int v[9], int *sum )
{
int i;
*sum = 0;
for ( i = 0; i <= 8; i++ )
*sum = *sum + v[i];
}
extern void vecref_
( int[], int * );
...
int i, sum;
int v[9] = ...
vecref_( v, &sum );
...
------------------------------
subroutine VecRef( v, total)
integer i, total, v(9)
total = 0
do i = 1,9
total = total + v(i)
end do
...
| Code Example 11-5 Passing a Two-Dimensional Array |
REAL Q(10,20)
...
Q(3,5) = 1.0
CALL FIXQ(Q)
...
------------------------------
void fixq_( float a[20][10] )
{
...
a[5][3] = a[5][3] + 1.;
...
}
extern void
qref_( int[][10], int *);
...
int m[20][10] = ... ;
int sum;
...
qref_( m, &sum );
...
------------------------------
SUBROUTINE QREF(A,TOTAL)
INTEGER A(10,20), TOTAL
DO I = 1,10
DO J = 1,20
TOTAL = TOTAL + A(I,J)
END DO
END DO
...
Code Example 11-6
Passing Fortran 77 STRUCTURE Records
|
STRUCTURE /POINT/
REAL X, Y, Z
END STRUCTURE
RECORD /POINT/ BASE
EXTERNAL FLIP
...
CALL FLIP( BASE )
...
------------------------------
struct point {
float x,y,z;
};
void flip_( v )
struct point *v;
{
float t;
t = v -> x;
v -> x = v -> y;
v -> y = t;
v -> z = -2.*(v -> z);
}
struct point {
float x,y,z;
};
void fflip_ ( struct point *) ;
...
struct point d;
struct point *ptx = &d;
...
fflip_ (ptx);
...
------------------------------
SUBROUTINE FFLIP(P)
STRUCTURE /POINT/
REAL X,Y,Z
END STRUCTURE
RECORD /POINT/ P
REAL T
T = P.X
P.X = P.Y
P.Y = T
P.Z = -2.*P.Z
...
| Code Example 11-7 Passing Fortran 90 Derived Types |
TYPE point
REAL :: x, y, z
END TYPE point
TYPE (point) base
EXTERNAL flip
...
CALL flip( base)
...
------------------------------
struct point {
float x,y,z;
};
void flip_( v )
struct point *v;
{
float t;
t = v -> x;
v -> x = v -> y;
v -> y = t;
v -> z = -2.*(v -> z);
}
struct point {
float x,y,z;
};
extern void fflip_ (
struct point *) ;
...
struct point d;
struct point *ptx = &d;
...
fflip_ (ptx);
...
------------------------------
SUBROUTINE FFLIP( P )
TYPE POINT
REAL :: X, Y, Z
END TYPE POINT
TYPE (POINT) P
REAL :: T
T = P%X
P%X = P%Y
P%Y = T
P%Z = -2.*P%Z
...
pointer can be passed to a C routine as a pointer to a pointer because the Fortran routine passes arguments by reference. A Fortran 77 pointer is not equivalent, however, to a C char ** data type. Code Example 11-8
Passing Fortran 77 POINTER
|
REAL X
POINTER (P2X, X)
EXTERNAL PASS
P2X = MALLOC(4)
X = 0.
CALL PASS(X)
...
------------------------------
void pass_(x)
int **x;
{
**x = 100.1;
}
extern void fpass_;
...
float *x;
float **p2x;
fpass_(p2x) ;
...
------------------------------
SUBROUTINE FPASS (P2X)
REAL X
POINTER (P2X, X)
X = 0.
...
%VAL(arg) as an argument in the call.
x by value and y by reference. The C routine incremente both x and y, but only y is changed:
| Code Example 11-9 Passing Simple Data Arguments by Value: Fortran 77 Calling C |
REAL x, y
x = 1.
y = 0.
PRINT *, x,y
CALL value( %VAL(x), y)
PRINT *, x,y
END
-----------------------------------------------------------
void value_( float x, float *y)
{
printf("%f, %f\n",x,*y);
x = x + 1.;
y = y + 1.;
printf("%f, %f\n",x,*y);
}
-----------------------------------------------------------
Compiling and running produces output:
1.00000 0. x and y from Fortran
1.000000, 0.000000 x and y from C
2.000000, 1.000000 new x and y from C
1.00000 1.00000 new x and y from Fortran
Functions that Return a Value
A Fortran function that returns a value of type BYTE (Fortran 77 only), INTEGER, REAL, LOGICAL, DOUBLE PRECISION, or REAL*16 (SPARC and PowerPC only) is equivalent to a C function that returns a compatible type (see Table 11-1 and Table 11-2). There are two extra arguments for the return values of character functions, and one extra argument for the return values of complex functions.
REAL or float value. BYTE, INTEGER, LOGICAL, DOUBLE PRECISION, and REAL*16 are treated in a similar way:Code Example 11-10
Functions Returning a Value - REAL and float
|
real ADD1, R, S
external ADD1
R = 8.0
S = ADD1( R )
...
------------------------------
float add1_( pf )
float *pf;
{
float f ;
f = *pf;
f++;
return ( f );
}
float r, s;
extern float fadd1_() ;
r = 8.0;
s = fadd1_( &r );
...
------------------------------
real function fadd1 (p)
real p
fadd1 = p + 1.0
return
end
COMPLEX Data
COMPLEX or DOUBLE COMPLEX is equivalent to a C function with an additional first argument that points to the return value in memory. The general pattern for the Fortran function and its corresponding C function is:|
Fortran function |
C function |
COMPLEX FUNCTION CF(a1, a2, ..., an) |
cf_ (return, a1, a2, ..., an) struct { float r, i; } *return;
|
Code Example 11-11
Function Returning COMPLEX (Fortran 77 only)
|
COMPLEX U, V, RETCPX
EXTERNAL RETCPX
U = ( 7.0, -8.0)
V = RETCPX(U)
...
------------------------------
struct complex { float r, i; };
void retcpx_( temp, w )
struct complex *temp, *w;
{
temp->r = w->r + 1.0;
temp->i = w->i + 1.0;
return;
}
struct complex { float r, i; };
struct complex c1, c2;
struct complex *u=&c1, *v=&c2;
extern retfpx_();
u -> r = 7.0;
u -> i = -8.0;
retfpx_( v, u );
...
------------------------------
COMPLEX FUNCTION RETFPX(Z)
COMPLEX Z
RETFPX = Z + (1.0, 1.0)
RETURN
END
COMPLEX function type is incompatible with this implementation.
CHARACTER String
|
Fortran function |
C function |
CHARACTER*n FUNCTION C(a1, ..., an) |
void c_ (result, length, a1, ..., an) char result[ ]; long length; |
Code Example 11-12
Function Returning CHARACTER String
|
CHARACTER STRING*16, CSTR*9
STRING = ' '
STRING = '123' // CSTR('*',9)
...
------------------------------
void cstr_( char *p2rslt,
int rslt_len,
char *p2arg,
int *p2n,
int arg_len )
{ /* return n copies of arg */
int count, i;
char *cp;
count = *p2n;
cp = p2rslt;
for (i=0; i<count; i++) {
*cp++ = *p2arg ;
}
}
void fstr_( char *, int,
char *, int *, int );
char sbf[9] = "123456789";
char *p2rslt = sbf;
int rslt_len = sizeof(sbf);
char ch = '*';
int n = 4;
int ch_len = sizeof(ch);
/* make n copies of ch in sbf
*/
fstr_( p2rslt, rslt_len,
&ch, &n, ch_len );
...
------------------------------
FUNCTION FSTR( C, N)
CHARACTER FSTR*(*), C
FSTR = ''
DO I = 1,N
FSTR(I:I) = C
END DO
FSTR(N+1:N+1) = CHAR(0)
END
COMMON
COMMON can be emulated in C by using a global struct:Code Example 11-13
Labeled COMMON
|
COMMON /BLOCK/ ALPHA,NUM
...
extern struct block {
float alpha;
int num;
};
extern struct block block_ ;
main ()
{
...
block_.alpha = 32.;
block_.num += 1;
...
}
The Fortran I/O library is implemented largely on top of the C standard I/O library. Every open unit in a Fortran program has an associated standard I/O file structure. For the stdin, stdout, and stderr streams, the file structure need not be explicitly referenced, so it is possible to share them.
If a Fortran main program calls C to do I/O, the Fortran I/O library must be initialized at program startup to connect units 0, 5, and 6 to stderr, stdin, and stdout, respectively. The C function must take the Fortran I/O environment into consideration to perform I/O on open file descriptors.
However, if a C main program calls a Fortran subprogram to do I/O, the automatic initialization of the Fortran I/O library to connect units 0, 5, and 6 to stderr, stdin, and stdout is lacking. This connection is normally made by a Fortran main program. If a Fortran function attempts to reference the stderr stream (unit 0) without the normal Fortran main program I/O initialization, output will be written to fort.0 instead of to the stderr stream.
The C main program can initialize Fortran I/O and establish the preconnection of units 0, 5, and 6 by calling the
f_init() Fortran 77 library routine at the start of the program and, optionally, f_exit() at termination.Remember: even though the main program is in C, you should link with f77.
The Sun Fortran implementation returns the
int value of the expression on the RETURN statement. This is superbly implementation dependent and its use is not recommended:| Code Example 11-14 Alternate Returns (Obsolete) |
int altret_ ( int * );
main ()
{
int k, m ;
k =0;
m = altret_( &k ) ;
printf( "%d %d\n", k, m);
}
------------------------------
SUBROUTINE ALTRET( I, *, *)
INTEGER I
I = I + 1
IF(I .EQ. 0) RETURN 1
IF(I .GT. 0) RETURN 2
RETURN
END
demo% cc -c tst.c
demo% f77 -o alt alt.f tst.o
alt.f:
altret:
demo% alt
1 2
The C routine receives the return value 2 from the Fortran routine because it executed the RETURN 2 statement.