Chapter 32: Using Aldor with Fortran-77
32.1 : Basics
This section describes how to call subprograms written in Fortran-77
from Aldor, and how to call routines written in Aldor from
Fortran-77. Please note that this interface is still under
development. Since there is no standard foreign-language interface to
Fortran-77 it may be necessary to customise your implementation of
Aldor to work with your local Fortran compiler. As is the case
with Aldor programs which import other foreign code, programs
which use Fortran cannot be run in the interpreter environment.
32.2 : Simple Example
This simple example demonstrates the main concepts you need to know to
call Fortran from Aldor. It shows the use of a Fortran routine
for sorting all or part of a vector of floating-point numbers.
32.3 : Data Correspondence
Fortran-77 has a fixed and relatively small set of data types, and
passes all subprogram parameters by reference (i.e. it passes a
pointer to the data rather than a copy of the data). Aldor, on the
other hand, has a rich and extensible type system, and in general will
pass copies of subprogram data (at least in simple cases). The aim
of the interface is to ensure that Foreign functions behave naturally
in their host environment.
32.4 : Calling Aldor Routines from Fortran
This is very similar to the way Aldor routines can be passed to C
functions. There is one restriction --- exported functions must be
defined in the top level of a file, not within an add-body. Of course
the exported function itself may use other functions defined in add
bodies so this is not really a problem.
For example, consider the Aldor program:
32.5 : Platform-dependent details
The foreign language interface of Fortran is not standardised, and so
there are some details which vary from platform to platform. It is
possible to tailor an installation of Aldor to a particular
Fortran compiler by editing the file
$ALDORROOT/include/aldor.conf and setting the values of the
following keys:
32.6 : Larger Examples
The following code calls the NAG library function d01ajf (a
numeric integration routine) with values specified on the command
line. For clarity, all the parameters are named. For details of what
the various arguments do please see the NAG Fortran Library Manual,
which is available in both printed and electronic versions.
The current interface supports all of the data types in Fortran-77,
and allows Fortran and Aldor functions to be used interchangeably in
many contexts. For example a Fortran function can be treated as an
Aldor function transparently, and an Aldor routine may behave
like a Fortran Function or Subroutine. The only major restriction is
that an Aldor program cannot see a Fortran Common block, and a
Fortran program has no way of accessing Aldor global variables.
The program could be compiled as follows, assuming that the
fsort routine is contained in a library called sort.
$ aldor -Cfortran -Fx -laldordemo -Clib=sort f77sort.as
Note the use of the -Cfortran flag. This has the effect of
causing the linker to link to the appropriate Fortran runtime
routines, and may also cause compiler-specific initialisation code to
be generated. It is not necessary to use this flag except at the link
stage.
Non-Array Arguments
The current correspondence between Fortran and Aldor types is as
follows:
Fortran Aldor
INTEGER SingleInteger REAL SingleFloat DOUBLE PRECISION DoubleFloat CHARACTER SingleInteger CHARACTER(*) String/FortranString COMPLEX REAL FSComplex COMPLEX DOUBLE FDComplex LOGICAL Boolean
Fortran strings (or rather Character arrays) are not null-terminated,
so to manipulate them it is necessary to know their length. Data from
the Aldor domain String is automatically converted to the
equivalent Fortran object, which is in principal a (length, data)
pair. The FortranString type uses this Fortran representation
directly.
By default Aldor passes a copy of a scalar parameter to Fortran,
in line with its usual semantics. Since many Fortran routines return
results by modifying their arguments there is also a Ref
constructor which will in effect copy the value of the parameter at
the end of the call to the Fortran routine back to the Aldor
object (this is often referred to as ``copy-in/copy-out'' semantics).
The choice of whether to declare an argument to a Fortran routine to
be e.g. a DoubleFloat or a Ref(DoubleFloat) is left up
to the user, and will depend on whether he or she wishes to inspect
its value after the call to Fortran has been completed. Note that it
is perfectly safe to declare an argument as e.g. DoubleFloat
even if it will be modified by Fortran, although of course the
modified value will not be visible in Aldor.
In practice it would be very restrictive only to be allowed to pass this
fixed list of types to Fortran, so Aldor uses a set of categories
to indicate that a domain can be used to pass particular types of
values. This usually means that the domain's representation is the
appropriate basic machine type or a pointer to it. The categories
are:
FortranInteger INTEGER FortranReal REAL FortranDouble DOUBLE PRECISION FortranLogical LOGICAL FortranString CHARACTER(*) FortranComplexReal COMPLEX REAL FortranComplexDouble COMPLEX DOUBLE
Array Arguments
A normal array of Aldor data cannot be passed directly to Fortran
for a number of reasons. The current solution is to use five special
domains as follows to pass vectors (i.e. one-dimensional arrays):
Multi-dimensional arrays are supported through the type constructor
FMultiDimensionalArray and the domain
FMultiDimensionalArrayData. FMultiDimensionalArray
takes a Fortran type and its corresponding scalar array type and
produces a multidimensional version. For example, the following
creates a two-dimensional array of complex double precision numbers
and initialises it.
FMDA ==> FMultiDimensionalArray;
ARR ==> FMDA(FDComplex, FComplexDoubleArray);
arr: ARR := new [10, 10];
for i in 1..10 repeat for j in 1..10 repeat
arr[i, j] := 1/(i+j-1);
Any multi-dimensional array arguments to a Fortran function should be
declared to be of type `FMultiDimensionalArrayData'. The
multi-dimensional array type provides the method `data' for
converting to FMultiDimensionalArrayData. For example:
import {
fort:FortranMultiDimensionalArrayData -> ()
} from Foreign Fortran;
a:FMultiDimensionalArray(DoubleFloat,FDoubleArray);
a := new [4,4];
fort(data a);
A more elegant mechanism for passing arrays to Fortran will be
introduced in a future release of Aldor.
Passing Subprograms as Arguments
The use of Fortran Function and Subroutine arguments is
supported. For example:
DF ==> DoubleFloat;
import {
integrate: (DF -> DF, hi: DF, lo: DF) -> DF;
} from Foreign Fortran;
pi := 3.14;
integrate(sin, 0.0, pi);
-- integrate the cos of function between 0 and 1
foo(fn: DF -> DF): DF == {
integrand(x: DF): DF == cos fn x;
integrate(integrand, 0.0, 1.0);
}
In the examples, `integrate' is being used with functions which
are either exported from a domain or package, or locally defined.
There is one restriction on the way dummy procedures can be used. An
Aldor function which calls a Fortran routine cannot be invoked
recursively by the call to the Fortran routine.
#include "aldor"
#include "aldordemo"
import from IntegerPrimesPackage, Integer;
export {ISPRIM:SingleInteger -> Boolean} to Foreign Fortran;
ISPRIM(n:SingleInteger):Boolean == prime?(n::Integer)
This creates a function isprim which can be called from
Fortran. Note that the name of the function must be legal in the
Fortran world (strictly speaking this means no more than six
characters and uppercase, although most modern compilers take a more
relaxed view!). A Fortran routine to call this function might look
like:
INTEGER I
LOGICAL ISPRIM, B
EXTERNAL ISPRIM
C
DO 100 I=1,100
B=ISPRIM(I)
IF (B) THEN
PRINT*,I," is prime"
ENDIF
100 CONTINUE
C
END
Notice that in Fortran only the return type is given, and that the
data type correspondence is the same as that provided earlier.
To compile and link the routines together we might do the following:
$ aldor -Fo isprime.as
$ f77 testprime.f isprime.o -L$ALDORROOT/lib -laldordemo -laldor -lfoam
Note that as well as linking to the appropriate Aldor libraries it
is important to link to the foam library.
The configuration file is discussed in more detail in Chapter 28.
#include "aldor"
DF ==> DoubleFloat;
SI ==> SingleInteger;
-- an exception type.
D01AjfError(n: SI): with == add;
import { d01ajf:(f: DoubleFloat -> DF,
a: DF, b: DF,
epsabs: DF, epsrel: DF,
result: Ref DF,
abserr: Ref DF,
w: FDoubleArray,
lw: SI,
iw: FSingleIntegerArray,
liw: SI,
ifail: Ref SI) -> ();
} from Foreign Fortran;
integrate(f:DF -> DF, lo:DF, hi:DF, eps:DF, lw:SI):DF == {
w: FDoubleArray := new(lw);
iw: FSingleIntegerArray := new(lw quo 4);
ifail: SI := 0;
result: DF := 0.0;
abserr: DF := 0.0;
d01ajf(f, lo, hi, eps, -1.0, ref result, ref abserr, w,
lw, iw, lw quo 4, ref ifail);
if not zero? ifail then except D01AjfError(ifail);
result;
}
import from CommandLine, NumberScanPackage(DF), SI,
FormattedOutput, Machine, Array String;
integrand(x: DF): DF == 4.0/(1.0+x*x);
if (#arguments = 3) then {
arg0: DF := scanNumber arguments.1;
arg1: DF := scanNumber arguments.2;
arg2: DF := scanNumber arguments.3;
res := integrate(integrand, arg0, arg1, arg2, 2000);
print."Integral from: ~a to ~a (acc: ~a)~nResult: ~a~n"_
(<< arg0, << arg1, << arg2, << res);
}
else
print."Please specify 3 arguments (lo, hi, accuracy)~n"();
Previous
Home
Contents