If one wants to use FORTRAN libraries from C in the UNIX environment, there are several problems: FORTRAN routines always expect pointers to the arguments (call by reference) even if the values are not altered. Calling the FORTRAN routine with a constant value, for example, is not directly possible from C. You first have to assign the constant to a temporary variable and then call the FORTRAN routine with the pointer to that dummy. If the FORTRAN routine has many arguments the resulting C code looks ugly with all the assignments and "&" in front of arguments. In addition it hides the fact that certain arguments are not altered by the FORTRAN routine.
IBM supplies on the RS6000/AIX a header file
"essl.h" that simplifies the use of the
(FORTRAN) library ESSL from C. The assignment of
arguments to temporary variables and the passing of the pointers is hidden
from the user using macros of the C preprocessor so that calls
to library function can be written in a natural way. The program
"flib2c" produces header files that work in
the same manner as "essl.h". Additionally I have
corrected a little flaw in "essl.h": The temporary
variables therein are global ones, so if one has several source codes that
include "essl.h", the compiler may generate a
"Multiply defined symbol" error. Furthermore, since
static variables have a limited scope (i.e. they are local to the file)
the compiler can do improved optimizations: It may not keep their values
because they can not be used outside and so the global optimizer can
delete unused temporaries.
flib2c work?flib2c sourcefile.def destfile.h
It produces the header file "destfile.h" from the
definition file "sourcefile.def" (I recommend using
the extension ".def" for
"sourcefile"). For possible command line options see
section on command line options.
The definition file may include C like comments that are ignored,
lines starting with "!" that are copied verbose to the
output file (except the "!"). Finally the definition
file will contain prototypes for the FORTRAN library functions. They
look as common ANSI-C prototypes: The (optional) word
"extern" is followed by the function type, the function
name and the arguments in parenthesis. If the function type is omitted the
function is assumed to be of type "void" (this differs
from C but makes sense since no type normally refers to FORTRAN
SUBROUTINEs that have no return value). Types for the function
arguments may be given explicitly or can be derived from the first letter with
implicit rules. The default implicit rules are the same as in
FORTRAN: Argument names starting with a-h and
o-z are assumed to be float (REAL*4 in
FORTRAN) and names starting with i-n are
int (INTEGER). These rules may be altered with a
"preprocessor" command '#implicit' (see below in the
section about the preprocessor).
The conversion from the ".def" file to the
corresponding ".h" file has only to be done once for a
certain library; the end user needs only access to the
".h" file (which should be stored in
"/usr/local/include", for example).
In the following I give some examples that further explain the mode of
operation of flib2c.
test1 has real arguments x,
y and z and integer arguments i,
j and k. The function returns no value
("SUBROUTINE" in FORTRAN). In a typical
FORTRAN program one would call the routine in the following way:
CALL TEST1(x,y,z,i,j,k)The input file "test1.def" for flib2c contains the single line
test1(x,y,z,i,j,k)The command flib2c test1.def test1.h creates the header file "test1.h":
#ifndef __test_h__
/* ******************************************************************** */
/* ***** This file is generated by FLIB2C (C) Joerg Schoen 1993 ***** */
/* ***** from test1.def ***** */
/* ***** This file is stand alone. ***** */
/* ******************************************************************** */
#define __test_h__ 1
#ifdef __cplusplus /* C++ compatibility */
extern "C" {
#endif
/* ********** Temporary variables for fortran linkage ********** */
static int __testi1,__testi2,__testi3;
static float __testf1,__testf2,__testf3;
/* ******************* PROTOTYPES ************************ */
extern void test1_(float *x,float *y,float *z,
int *i,int *j,int *k);
/* ******** Now defines for linkage to fortran library ************ */
#define test1(x,y,z,i,j,k) \
(__testf1 = (x), __testf2 = (y), __testf3 = (z), __testi1 = (i), \
__testi2 = (j), __testi3 = (k), \
test1_(&__testf1,&__testf2,&__testf3,&__testi1, \
&__testi2,&__testi3) )
#ifdef __cplusplus
}
#endif
#endif /* End of machine generated file */
The prefix for all temporary variables is generated from the name of the
header file as "test1". The whole file is included in a
#ifdef - #endif construct with the symbol
__test that allows multiple inclusion of
"test1.h" without problems. Note that all temporaries
start with "__" conforming to the ANSI standard so that
accidental matching with user defined names is prohibited. For C++
compatibility the definitions will be bracketized correctly.
We need three temporary integers and three reals, so we define them as static variables. Then the correct prototype for the FORTRAN routine is given: All arguments must be pointers to the correct types. That allows the C compiler to check for wrong calling sequences (this will be explained further in example 2, it does not apply in this special example).
The actual call in C of "test1" is redirected with a
#define construct of the C preprocessor: After all arguments are
assigned to the static variables the FORTRAN routine is called with
pointers to all the statics. Note that the #define construct
looks as a recursive call (it contains a call to test1) but this
is valid in ANSI C. The whole sequence of assignments is written with the
comma operator so that the function call can be used within ordinary C
expressions. The value of a comma expression is the value of the last
subexpression, which is correct since this is the return value of the function
call (in our example the function has no return value).
On certain UNIX systems one has to append an underscore to each
FORTRAN name. flib2c can append an arbitrary extension to
the FORTRAN call with the command line option
"-p" (see section on command line
options). This means that the user does not need to know the specifics of
his system; he calls the routine with test1(...) without any name
extension. The header file for his system hides this fact in the define
construct. Note that this will produce portable programs -- only the header
file is different for different operating systems. Also the
".def" file is the same for all operating systems.
If one does not use the implicit rules, the input in the preceding example would be written as:
extern void test1(float a,float b,float c,int i,int j,int k);The key word
extern at the start of the line and the
";" at the end were kept for compatibility reasons (It
looks much like C code).
The FORTRAN routine test2 has the real arguments
x and y, the integer argument i and
additionally the vector (or array) a of reals and the
vector/array ivec of integers. It returns a REAL*4
value. The FORTRAN definition looked like REAL*4 FUNCTION
TEST2(...).
The input file "test2.def" contains the line
float test2(x,*a,y,*ivec,i)or more verbose
float test2(float x,float *a,float y,int *ivec,int i)
The header file "test2.h" after calling flib2c test2.def test2.h now contains (common lines from the first example are dropped):
....... extern float test2(float *x,float *a,float *y, int *ivec,int i); ........ #define test2(x,a,y,ivec,i) \ (__testf1 = (x), __testf2 = (y), __testi1 = (i), \ test2(&__testf1,a,&__testf2,ivec, \ &__testi1) ) .......
The difference between the arguments x and y and
the vector a (i and the vector ivec)
can not be recognized from the prototype. Looking at the #define
construct reveals that x, y and i are
assigned to temporaries while a and ivec are passed
directly since they are already pointers of the correct types. Now the
prototype is useful for the compiler for discovering wrong calling sequences:
Passing for a or ivec a pointer of the wrong
type. Note also that the user can pass an integer (or double) value for
x without running into trouble: The assignment __testf1 =
x instructs the compiler to cast the value to the correct type
float.
The case when a FORTRAN routine expects an character variable for an argument has to be treated separately.
Assume the input file 'test3.def' contains the line
test3(x,char *s,y)flib2c generates the header file "test3.h":
....... /* ******** We need the strlen function ******** */ #include....... static char *__tests1; ....... extern void test3(float *x,char *s,float *y,int); ....... #define test3(x,s,y) \ (__testf1 = (x), __tests1 = (s), __testf2 = (y), \ test3_(&__testf1,__tests1,&__testf2,strlen(__tests1) \ ) ) .......
The FORTRAN specifications for the UNIX systems I'm aware of state
that a FORTRAN routine with a character argument gets the pointer to
the character vector and an invisible integer argument at the end of the
parameter list. The integer argument contains the length of the character
vector. The prototype for test3 therefore has an integer argument
as the last parameter (Note that the integer is passed by value, not by
reference). The character string argument s is copied to a local
variable too, because it is needed twice in the define macro and copying
prevents the compiler from evaluating the actual expression for s
more than once (it may contain side effects as in s++). In the
call of test3 we supply the length of the string generated with
the standard library function strlen. To be clean we further
include string.h. Also it is possible that string.h
directs the compiler to actually inline the strlen function call
which results in faster code. Note that this method will only work when
passing input parameters in the character array (for example constant
strings); if the character array is uninitialized, the strlen function may
yield wrong results.
Since many documentations of FORTRAN libraries use parameter names whose types are according to the implicit rules of FORTRAN I have implemented an automatic type recognition. This saves much work because one often can directly copy the prototypes from the FORTRAN documentation and only has to pay attention if arguments are needed for returning values.
Analogous to the IMPLICIT statement in FORTRAN
there is a preprocessor command #implicit in flib2c
that can be used to alter the inherent implicit rules or to switch them off
totally (Note that the name preprocessor is misleading; it does no
kind of preprocessing. The name was given because the commands start with
"#" that reminds one of the C preprocessor).
They look similar to the FORTRAN equivalents (beside the
"#"at the beginning of the line). The must appear in
separate lines (as in C) and the "#" must appear in the
first column (not as in C).
The special command
#implicit nonedeletes all implicit rules. Following attempts to use untyped arguments will result in an error. As an example, the command
#implicit double(a-h,t-u),int(i-n),char*(s),float(o-r)reserves the letter
s for character strings and the other letter
accordingly.
Beside the source and destination file the command line may contain some options for flib2c on arbitrary positions. Options always start with "-".
test
is to be called, the correct name for C is name_ on some
operating systems (e.g. Silicon Graphics). Other compilers on the
RS6000 for example do not append any extension on FORTRAN
names. To hide this implementations specifics from the user one calls
flib2c with the appropriate option. Note that the default
behaviour of flib2c can be seen with
flib2c -h. The source code contains some lines that set the
default depending on the machine it is compiled. This means that user
programs and the .def files are portable across different
machines while the ".h" files are not. They have
to be generated separately for every machine.
a-h and o-z
double (default was float) and i-n
is integer.
float instead of
double. In the present version this is the default.
If a FORTRAN routine uses a non-pointer argument for returning a result, this argument has to be declared as a pointer in the ".def" file, because the user must supply the pointer to a correct typed variable. Otherwise the FORTRAN routine will return a value that is stored in one of the temporary variables and the user can not access them. In summary one has to treat simple variables that will be used for results in the same way as pointers.
In the current version flib2c supports only the types
float' (REAL*4), double
(REAL*8), int (INTEGER) and character
strings char* for input. Other types as char have
not been implemented yet.
Also FORTRAN routines that return character arrays are not handled
correctly. A crude support for complex data types has been implemented too,
the single and double precision complex types are named cmplx and
dcmplx. They are represented with structures.
It is not possible to use reserved words of the C language for variable names, since at least the C compiler may reject the code.
Two examples have been included to illustrate certain features of
flib2c, especially using the "!" and
#implicit commands. They may not be very useful for users that do
not know TWGRAPH or IMSL.
This program is Copyright © 1993-1995 by Joerg Schoen. Permission to use, copy and distribute this software together with its documentation for any purpose is herby granted provided that the above copyright notice appears in all copies. No fee must be taken for this package. This software is provided "as is" without expressed or implied warranty.
Bug reports, questions, improvements, mail to Jörg Schön