Previous Home Contents

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.

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.

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
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.

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
CHARACTER SingleInteger
CHARACTER(*) String/FortranString

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
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 := 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.

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
       DO 100 I=1,100
         IF (B) THEN
            PRINT*,I," is prime"
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
  $ 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.

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:

The configuration file is discussed in more detail in
Chapter 28.

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);

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);
  print."Please specify 3 arguments (lo, hi, accuracy)~n"();

Previous Home Contents