1-11 FORTRAN/C INTEROPERABILITY
********************************
(Thanks to Craig Burley for the comments)
(partially based on the Fortran FAQ)
FORTRAN is well suited to numerical computations, C is suitable for
system programming tasks. A combination of the two languages in one
program should have been the best solution for some programs.
It seems simple enough, we can easily create object modules from both
FORTRAN and C code, the object modules will be surely compatible if
made with the native compilers, then we can link edit them together.
Unfortunately some issues make this seemingly simple interfacing
highly platform dependent.
Note that for some systems, you have to initialize a runtime system
(shared libraries) explicitly if you call a different language,
and stopping execution from the other program may not work properly.
Cfortran package
----------------
One solution is to use the amazing 'cfortran' package, an include
file that performs the interfacing using the C language preprocessor.
It takes some time to study the documentation, and it may overtax
the preprocessor.
The cfortran package makes Fortran/C interfacing quick and easy
for a wide range of platforms. The most recent version of the
include file "cfortran.h", and the documentation is available
via anonymous ftp from:
ftp://zebra.desy.de/cfortran
and via www at http://www-zeus.desy.de/~burow .
Manual interfacing
------------------
If you prefer to do it manually, look first in your COMPILER
DOCUMENTATION, Sun and IRIX have a lot to say on this subject.
A multi-platform (with some minor changes) example can be found in
the chapter on "variable size arrays".
There are some general issues you must be aware of before starting
such work:
Routines names
--------------
On some machines (e.g. UNIX) the FORTRAN compiler appends a trailing
underscore to FORTRAN routine names, both in subroutine/function
definitions and when calling them.
Why add the underscore suffix? Possible explanations are:
o Prevent name clashes of user-written routines with the
routines in the system libraries used by the compiler
at link time (the routine names in these libraries
usually don't have a trailing underscore).
For example, UNIX system routines may have simple names,
that may be used in a user program.
o Prevent "amateurish" mixed-language programming.
Without proper understanding these attempts may produce
erroneous results.
In mixed-language programs on such machines, the linker will have a
problem when trying to match routine calls and routine code in the
object code.
Either the FORTRAN CALL statement (with underscores appended) will
reference a non-FORTRAN routine, or the other language may call a
FORTRAN routine (also with underscores appended). In each case
there will be a mismatch, which can be avoided by manually
supplying the required underscores:
o If a FORTRAN routine calls a routine written another
language, append a trailing underscore IN THE DEFINITION
(in the code) of the non-FORTRAN routine.
o If a non-FORTRAN routine calls a FORTRAN routine,
append an underscore in THE CALLING STATEMENT to
the FORTRAN routine name.
Compilers which behave like that may transform external user-defined
names in the following way:
Appends a trailing underscore Name remains unaffected
----------------------------- -----------------------
Main program name Blank COMMON name '_BLNK__'
Named COMMON blocks Intrinsic functions names
BLOCKDATA subprograms
All names implicitly or
explicitly declared EXTERNAL
Check your compiler documentation, you may not have to add anything,
if your compiler/linker does it another way.
For example, VMS differentiates between user and system defined names
by having the later contain a '$' sign, users are advised not to use
that sign in names.
There may be a similar problem if the compiler/linker changes the case
of routine names (e.g. CRAY uppercasing).
CALLING C ROUTINES FROM FORTRAN
===============================
Machine C Routine name
------- ------------------
ALPHA/DUNIX lowercase letters_ (default)
ALPHA/VMS anything
CRAY UPPERCASE LETTERS
HP lowercase letters (?)
IBM/AIX lowercase letters (all IBM's ?)
SGI/IRIX lowercase letters_
SUN lowercase letters_
TITAN UPPERCASE LETTERS
VAX/VMS anything
VAX/ULTRIX lowercase letters_ (default)
Variable passing mechanisms
---------------------------
Fortran usually passes its variables by reference (passes pointers).
That means that you MUST give addresses in your calling C-program.
Function results, may be passed by value, for example, the following
code calls a FORTRAN function called "gamma":
double x, y;
..................
x = 2.0;
y = gamma_(&x)
Array storage order
-------------------
Fortran uses a column wise storage of matrices while C stores them
row wise. This means that when you want to pass a matrix from your
C-program to the fortran routine you must transpose the matrix
in your program before entering the routine. Of course, any output
from such a routine must be transposed again.
If you omit this step, then probably your program will run (because
it has data to compute on) but it will generate wrong answers.
Transposition is expensive, you may avoid it by adapting the source
code, when the FORTRAN source says A(j+1,i+1) it could mean a[i][j] in C,.
There are times when you don't want to modify already existing FORTRAN
and C code, in such cases you may have to write a transposition wrapper.
This can be advisable for reasons of clarity (i.e. keeping the
documentation the code and the math in sync.)
Array indexes
-------------
Remember that array indexes in Fortran starts by default at 1 while
in C they start at index 0; hence a passed array fortran_array[1:100]
must be used in the C-routine/program as c_array[0:99].
Variable type matching
----------------------
Watch out especially with float's and double's. Make sure that the
size of the variable in the calling program is identical to the size
in the Fortran routine:
float --- REAL (this is typical, but not always the case)
double --- REAL*8
Character strings
-----------------
FORTRAN may pass string lengths BY VALUE in the order the strings appear
in the argument list. These will NOT appear in the FORTRAN argument list,
but will appear in the C argument list.
You will need to take care of nulls and blanks spaces explicitly if you
ignore this ...
Some untested advice
--------------------
If you have the Fortran source code (of any routine) then on some
platforms you may use compiler directives specifying that the
Fortran compiler should use row wise storage. Some platforms support
these directives. However watch out with this if you call the same
routine from another Fortran routine/program.
Matching float types is extremely important on machines with little-
endian byte ordering. Parsing a float (C-routine) to a real*8 (Fortran)
number will not generate SEGV (SEGmentation Violation error) but give
wrong results as the data is parsed wrongly.
Mixing I/O on the same file descriptors may be problematic.
If you do ANY I/O in FORTRAN, you may have to use a FORTRAN main program.
The Sun FORTRAN compiler used lex and yacc to do the conversion of a
run time format from a character variable. If you use lex and yacc,
either rename the variables and functions or partially link before
you link to the FORTRAN libraries.
An example environment: the VMS Common Language Environment
-----------------------------------------------------------
VMS argument passing mechanisms are standardized by the VAX/ALPHA
procedure-calling standards, and inter-language calls are made simple,
a few FORTRAN language extensions makes it even simpler.
FORTRAN programs can use the 'builtin functions' to pass arguments
to C routines:
%REF(argument) Makes the argument pass by reference,
this is the default for numeric types.
Useful for strings!
%VAL(argument) Makes the argument pass by value.
Useful for numeric types!
program pssarg
integer int
character string*10
int = 8
string = 'apples'
call cstring(%val(int), %ref(string // char(0)))
end
#include
void cstring(int num, char *string)
{
printf(" %d %s\n", num, string);
return;
}
Another (but ugly) workaround for the FORTRAN/C string-passing
problem is to store the strings in BYTE arrays.
To go a little deeper, the only technical detail you have to learn
is the structure of the fixed-length string descriptor used to pass
character data:
struct descriptor /* VMS fixed length string */
{ /* descriptor used in FORTRAN */
unsigned short length;
unsigned char data_type, /* = 14 */
dsc_class; /* = 1 */
char *string_ptr;
};
Fortran compilers on UNIX machines don't use descriptors, instead a
"hidden" integer argument is added to the end of the argument list,
specifying the length of the string.
Two VMS examples that pass an INTEGER and a CHARACTER*10 string between
FORTRAN and C follow. All inter-language complications were 'pushed' to
the C code, the FORTRAN code is not 'aware' of them.
The hardcoding of descriptor.data_type and descriptor.dsc_class values
is of course bad programming practice. The proper header file (descrip.h)
should have been included and used.
The dimensional information of the CHARACTER*10 is contained in the
descriptor, so in the argument list you have to pass only the address
of the descriptor.
A VMS example (FORTRAN calling C)
---------------------------------
program for2c
integer n
character string*10
n = 123
string = 'abcdefghij'
write(*,*) ' In fortran: n= ', n
write(*,*) ' In fortran: string= ', string
call c_routine(n, string)
end
#include <stdio.h>
struct descriptor /* VMS fixed length string */
{ /* descriptor used in FORTRAN */
unsigned short length;
unsigned char data_type, /* = 14 */
dsc_class; /* = 1 */
char *string_ptr;
};
c_routine(int *n, struct descriptor *dsc)
{
int len;
char *string;
printf(" in C: n= %d \n", *n);
len = dsc->length;
printf(" in C: len= %d \n", len);
string = dsc->string_ptr;
printf(" in C: string= %*.*s \n", len, len, string);
return;
}
Another VMS example (C calling FORTRAN)
---------------------------------------
#include <stdio.h>
#include <string.h>
struct descriptor /* VMS fixed length string */
{ /* descriptor used in FORTRAN */
unsigned short length;
unsigned char data_type, /* = 14 */
dsc_class; /* = 1 */
char *string_ptr;
};
void f_routine(int *, struct descriptor *);
main()
{
int n = 123;
char string[] = "abcdefghij";
static struct descriptor dsc;
printf(" in C: n= %d \n", n);
printf(" in C: string= %s \n", string);
dsc.length = strlen(string);
dsc.data_type = 14;
dsc.dsc_class = 1;
dsc.string_ptr = string;
f_routine(&n, &dsc);
}
subroutine f_routine (n, string)
integer n
character string*(*)
write(*,*) ' In fortran: n= ', n
write(*,*) ' In fortran: string= ', string
return
end
Return to contents page