How to call a Windows Fortran DLL from a perl script


Problem:

In a Perl script, it is sometimes desirable to call Fortran subroutines in a
dynamically linked library (DLL). This may be useful in cases where a library
of pre-written subroutines is available to perform specific tasks, where the
higher efficiency of a compiled language is required for a particular task or
where functionality is to be available in both Perl and user Fortran. On
Windows systems, the Win32::API module may be used to accomplish this. Although
Win32::API is well documented, it is not immediately obvious how to call a
Fortran subroutine from a Perl script and deal correctly with the subroutine
arguments, including arrays.

Solution:

The method described here shows how call a Fortran subroutine from Perl, passing input and output data using Perl variables.

INITIAL NOTES

Although this example is complete, the user should ideally first read the documentation for the Win32::API module. Before using this method it is wise to establish whether it is really necessary, since many tasks can be performed efficiently in Perl without needing to call functions from a DLL.

CREATING A DLL

The example DLL was created using the CFX cfx5mkext utility. For example:

cfx5mkext -name test1 routine1.F

This creates the following symbol: _ROUTINE1@16
Other methods of creating the library may create symbols of different formats.

CREATING A NEW Win32::API OBJECT

The first step in the Perl is to create a new Win32::API object for the subroutine to be called. The line below demonstrates this.

my $subroutine1 = new Win32::API('test1', '_ROUTINE1@16', [qw(P P P P)], 'V');

Arguments specified when creating the new object:

- Name of the DLL (in this case the file name is test1.dll)
- Name of the subroutine (the symbol name must be used)
- Subroutine argument data types (All pointers (P))
- Return data type (in this case no return data hence void (V))

Further notes:

- For a Fortran subroutine, arguments are pointers, return is void
- Function naming conventionis for DVF compiler called from cfx5mkext
other compilers may differ (a freely available application called
'dependency walker' may be used to examine the function names in a DLL
file).

INITIALISING THE SUBROUTINE ARGUMENTS

The Perl 'pack' function is used to set all Fortran arguments from Perl variables. 'pack' is used to set a new Perl variable to contain the data in the format expected for a Fortran subroutine arguement. Since Fortran uses the 'pass by reference' method, the resulting data type needs to be a pointer. Output argumentsmust be given this treatment too, in order to allocate memory. Of course, some arguments may be used for both input and output. Examples for various Fortran data types are shown below:

Single integer:

my $size = 3;
my $p_size = pack('i',$size);

Single real (using literal for it's inital value):

my $ret = 1.0;
my $p_ret = pack('f',$ret);

Real array:

my @array1 = (1.0, 2.0, 3.0);
my $p_array1 = pack('f*',@array1);

CALLING THE SUBROUTINE

No catches here. The 'Call' method is used with all arguments. e.g.

$subroutine1->Call($p_size,$p_array1,$p_array2,$p_ret);

EXTRACTING DATA FROM THE SUBROUTINE ARGUMENTS

To extract values from the subroutine argument the Perl 'unpack' function is used. 'unpack' calls should correspond to the previous 'pack' calls.

For example, to recover a single real, 'unpack' would be used as follows:

$ret = unpack('f',$p_ret);

EXAMPLE

A complete example is provided, demonstrating the techniques described above. The attached zip archive file contails all files required.

See example call1.pl

The file test1.dll is a ready built DLL file, created from the subroutine routine.f, which simply returns the sum of all of the elements in two arrays.





Show Form
No comments yet. Be the first to add a comment!