Calling a Fortran routine from C is basically the reverse operation of
providing a C routine which is Fortran callable.
One has to know the correct spelling for the external
identifier, for numeric variables pass the address instead of the
value itself, and for CHARACTER
items construct the appropriate
argument list.
We continue to use the example of a subroutine with the same calling
sequence SUB(X,CH,Y)
.
In the following code fragments we assume that
REAL x, y; char* ch_ptr; int ch_len;
are already filled and that we want to pass their values to Fortran. Again the platform dependence in passing character data causes the main difficulty.
VMS
On VMS we have to fill the elements of the string descriptor and then pass its address:
#include <descrip.h> struct dsc$descriptor_s ch_dsc; ch_dsc.dsc$b_dtype = DSC$K_DTYPE_T; ch_dsc.dsc$b_class = DSC$K_CLASS_S; ch_dsc.dsc$w_length = ch_len; ch_dsc.dsc$a_pointer = ch_ptr; Sub( &x, &ch_dsc, &y );
Cray
On Cray a predefined macro packs the address and length information
into a cft77
descriptor:
#include <fortran.h> Sub( &x, _cptofcd(ch_ptr,ch_len), &y );
NeXT
Sub( &x, ch_ptr, &y, (sizeof x), ch_len, (sizeof y) );
Apollo ftn
The byte ordering of the Motorola-68k CPU does not allow the passing of
the address of a 4-byte int
if a 2-byte short
is expected.
Therefore for the Apollo ftn
compiler we have to use an
auxiliary short
:
short ch_ref = ch_len; Sub( &x, ch_ptr, &y, &ch_ref );
Others
For all other compilers except IBM-C/370 the call is straightforward:
Sub( &x, ch_ptr, &y, ch_len );
C/370
The IBM C/370 compiler does not allow to call a VS-Fortran
routine with CHARACTER
arguments.
The naive approach
#pragma linkage(SUB,FORTRAN) int x_size = (sizeof x); int y_size = (sizeof y); Sub( &x, ch_ptr, &y, &x_size, &ch_len, &y_size );
does not work. VS-Fortran passes the argument count implicitly by setting the sign bit of the last word in the argument block. uses argument blocks containing the reference addresses.
For routines with CHARACTER
arguments both the words at
positions N and 2N must be flagged.
The #pragma linkage
instructs the C compiler to
create a Fortran-style argument block with the last word flagged.
Unfortunately there is no way to set the sign bit also
in the middle of the argument block.
Attempts to patch the argument list such as
REAL* y_ref = (REAL*)(0x80000000 | (int)&y); Sub( &x, ch_ptr, y_ref, &x_size, &ch_len, &y_size );do not succeed. (It seems that C/370 clears the sign bit again before storing the address into the argument block.)
The work-around is to use a wrapper routine which receives the
character arguments as Hollerith data and copies them into
temporary CHARACTER
variables.
For example,
#pragma linkage(K77XCX,FORTRAN) #pragma linkage(SUB,FORTRAN) extern SUBROUTINE SUB; SUBROUTINE* sub_ptr = SUB; K77XCX( &sub_ptr, &x, ch_ptr, &ch_len, &y );
with
SUBROUTINE K77XCX(SUB,X1,K2,L2,X3) EXTERNAL SUB PARAMETER(MBIG=256) CHARACTER*(MBIG) C2 CALL UHTOC(K2,4,C2,L2) CALL SUB(X1,C2(:L2),X3) CALL FMEMCPY(K2,C2(:L2)) END
Obviously, MBIG
should be set to the maximum string length ever
expected.
FMEMCPY
is a Fortran callable version of memcpy
provided
by KUIP.
The CHARACTER
data has to be copied back into the C string in
case that it is an output parameter.
(UCTOH
cannot be used because there the number of bytes copied
is rounded up to the next multiple of 4.)
The disadvantage is that each type of argument list requires its own
wrapper.
This is not foreseen in the cfortran.h
approach and is the reason
why it cannot be used for C/370.