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.
One solution is to use the amazing 'cfortran' package, it takes some
effort to study the documentation, and may overtax your compiler.
Cfortran package
----------------
The cfortran package attempts to make Fortran/C interfacing "quick
and easy" for a wide range of platforms. The most recent version
of the include file "cfortran.h", which does the work, and the
documentation is available via anonymous ftp at:
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 the FORTRAN compiler appends a trailing underscore
to FORTRAN routine names both in routine definitions and in CALL
statements.
Why adding the underscore suffix? possible explanations are:
1) Prevent possible name clashes of user-written
routines with the routines in the system library
used by the compiler (if the routine names in
the library doesn't have a trailing underscore).
The system routines on UNIX have simple names
that are "tempting" to use in a user program.
2) Prevent "naive" attempts to do mixed-language
programming. Without real understanding such
attempts are doomed from the start.
In mixed-language programs on such machines, the linker will have a
problem when matching routine calls with routine code in the object
code. Either the FORTRAN CALL statement (with underscores appended)
may 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, that can be avoided by manually
supplying the required underscores:
1) 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.
2) If a non-FORTRAN routine calls a FORTRAN routine,
append an underscore in THE CALLING STATEMENT to
the FORTRAN routine name.
Such compilers 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).
This means that you MUST give addresses in your calling C-program.
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.
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
C ------------------------------------------------------------------
INTEGER
* INT
C ------------------------------------------------------------------
CHARACTER
* STRING*10
C ------------------------------------------------------------------
INT = 8
STRING = 'Apples'
CALL cstring(%VAL(INT), %REF(STRING // CHAR(0)))
C ------------------------------------------------------------------
END
#include
void cstring(int num, char *string)
{
printf(" %d %s\n", num, string);
return;
}
Another 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;
};
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 FOR_C
C ------------------------------------------------------------------
INTEGER
* N
C ------------------------------------------------------------------
CHARACTER
* STRING*10
C ------------------------------------------------------------------
N = 123
STRING = 'abcdefghij'
C ------------------------------------------------------------------
WRITE(*,*) ' IN FORTRAN: N= ', N
WRITE(*,*) ' IN FORTRAN: STRING= ', STRING
C ------------------------------------------------------------------
CALL c_routine(N, STRING)
C ------------------------------------------------------------------
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_RTN(&n, &dsc);
}
SUBROUTINE F_RTN (N, STRING)
C ------------------------------------------------------------------
INTEGER
* N
C ------------------------------------------------------------------
CHARACTER
* STRING*(*)
C ------------------------------------------------------------------
WRITE(*,*) ' IN FORTRAN: N= ', N
WRITE(*,*) ' IN FORTRAN: STRING= ', STRING
C ------------------------------------------------------------------
RETURN
END
Return to contents page