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.
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.
$ aldor -Cfortran -Fx -laldordemo -Clib=sort f77sort.asNote 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.
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.
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 |
FortranInteger | INTEGER |
FortranReal | REAL |
FortranDouble | DOUBLE PRECISION |
FortranLogical | LOGICAL |
FortranString | CHARACTER(*) |
FortranComplexReal | COMPLEX REAL |
FortranComplexDouble | COMPLEX DOUBLE |
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.
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.
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:
#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 ENDNotice 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 -lfoamNote that as well as linking to the appropriate Aldor libraries it is important to link to the foam library.
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.
#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"();