NAME

FFI::Platypus::Lang::Fortran - FFI::Platypus::Lang::Fortran

VERSION

version 0.14

SYNOPSIS

Fortran:

FUNCTION ADD(IA, IB)
    ADD = IA + IB
END

Perl:

use FFI::Platypus 2.00;

my $ffi = FFI::Platypus->new(
  api  => 2,
  lang => 'Fortran',
  lib  => './add.so',
);

$ffi->attach( add => ['integer*','integer*'] => 'integer');

print add(\1,\2), "\n";

DESCRIPTION

This module provides native types and demangling for Fortran when used with FFI::Platypus.

This module is somewhat experimental. It is also available for adoption for anyone either sufficiently knowledgeable about Fortran or eager enough to learn enough about Fortran. If you are interested, please send me a pull request or two on the project's GitHub.

For types, _ is used instead of *, so use integer_4 instead of integer*4.

These are some of the supported types:

byte, character
integer, integer_1, integer_2, integer_4, integer_8
unsigned, unsigned_1, unsigned_2, unsigned_4, unsigned_8
logical, logical_1, logical_2, logical_4, logical_8
real, real_4, real_8, double precision

EXAMPLES

The examples in this discussion are bundled with this distribution and can be found in the examples directory.

Passing and Returning Integers

Fortran

FUNCTION ADD(IA, IB)
    ADD = IA + IB
END

Perl

use FFI::Platypus 2.00;

my $ffi = FFI::Platypus->new(
  api  => 2,
  lang => 'Fortran',
  lib  => './add.so',
);

$ffi->attach( add => ['integer*','integer*'] => 'integer');

print add(\1,\2), "\n";

Execute

$ gfortran -shared add.f -o add.so
$ perl add.pl
3

Discussion

In Fortran 77 variables that start with the letter I are integers unless declared otherwise. Fortran is also pass by reference, which means that under the covers Fortran passes its arguments as pointers to the data, and you have to remember to pass in a reference to a value from Perl.

Here we are building our own Fortran dynamic library using the GNU Fortran compiler on a Unix like platform. The exact incantation that you will use to do this will unfortunately depend on your platform and Fortran compiler.

Calling a subroutine

Fortran

SUBROUTINE ADD(IRESULT, IA, IB)
    IRESULT = IA + IB
END

Perl

use FFI::Platypus 2.00;

my $ffi = FFI::Platypus->new(
  api  => 2,
  lang => 'Fortran',
  lib  => './sub.so',
);

$ffi->attach( add => ['integer*','integer*','integer*'] );

my $value = 0;
add(\$value, \1, \2);

print "$value\n";

Execute

$ gfortran -shared sub.f -o sub.so
$ perl sub.pl
3

Discussion

A Fortran "subroutine" is just a function that doesn't return a value. This example is similar to the previous and uses the same addition operation, but it returns the value in an argument instead of as the result of a function.

Calling recursive Fortran 90 / 95 Functions

Fortran

recursive function fib(x) result(ret)
  integer, intent(in) :: x
  integer :: ret
  
  if (x == 1 .or. x == 2) then
    ret = 1
  else
    ret = fib(x-1) + fib(x-2)
  end if

end function fib

Perl

use FFI::Platypus 2.00;

my $ffi = FFI::Platypus->new(
  api  => 2,
  lang =>'Fortran',
  lib  => './fib.so',
);

$ffi->attach( fib => ['integer*'] => 'integer' );

for(1..10)
{
  print fib(\$_), "\n";
}

Execute

$ gfortran -shared fib.f90 -o fib.so
$ perl fib.pl
1
1
2
3
5
8
13
21
34
55

Discussion

If you have a newer Fortran compiler that understands Fortran 90 or 95, you can take advantage of its advanced features like recursion and pointers. In this example we compute 10 Fibonacci numbers.

Complex numbers

Fortran

subroutine complex_decompose(c, r, i) 
  implicit none
  complex*16, intent(in) :: c
  real*8, intent(out):: r
  real*8, intent(out) :: i
  
  r = real(c)
  i = aimag(c)
end subroutine complex_decompose

Perl

use FFI::Platypus 2.00;
use Math::Complex;

my $ffi = FFI::Platypus->new(
  api  => 2,
  lang => 'Fortran',
  lib  => './complex.so',
);

$ffi->attach( complex_decompose => ['complex_16*','real_8*','real_8*'] );

complex_decompose( \(1.5 + 2.5*i), \my $r, \my $i);

print "${r} + ${i}i\n";

Execute

$gfortran -shared complex.f90 -o complex.so
$ perl complex.pl
1.5 + 2.5i

Discussion

Platypus now supports complex types of various sizes. This means that you can transparently use complex arguments and arrays of complex types.

Arrays

Fortran

subroutine print_array10(a)
  implicit none
  integer, dimension(10) :: a
  integer :: i
  
  do i=1,10
    print *, a(i)
  end do
  
end subroutine print_array10

Perl

use FFI::Platypus 2.00;

my $ffi = FFI::Platypus->new(
  api  => 2,
  lang => 'Fortran',
  lib  => './array.so',
);

$ffi->attach( print_array10  => ['integer[10]'] => 'void' );

my $array = [5,10,15,20,25,30,35,40,45,50];

print_array10($array);

Execute

$ gfortran -shared array.f90 -o array.so
$ perl array.pl
           5
          10
          15
          20
          25
          30
          35
          40
          45
          50

Discussion

In Fortran arrays are 1 indexed unlike Perl and C where arrays are 0 indexed. Perl arrays are passed in from Perl using Platypus as a array reference.

Multidimensional Arrays

Fortran

subroutine print_array2x5(a)
  implicit none
  integer, dimension(2,5) :: a
  integer :: i,n
  
  do i=1,5
    print *, a(1,i), a(2,i)
  end do

end subroutine print_array2x5

Perl

use FFI::Platypus 2.00;

my $ffi = FFI::Platypus->new(
  api  => 2,
  lang => 'Fortran',
  lib  => './array2d.so',
);

$ffi->attach( print_array2x5 => ['integer[10]'] => 'void' );

my $array = [5,10,15,20,25,30,35,40,45,50];

print_array2x5($array);

Execute

$ gfortran -shared array2d.f90 -o array2d.so
$ perl array2d.pl
           5          10
          15          20
          25          30
          35          40
          45          50

Discussion

Perl does not generally support multi-dimensional arrays (though they can be achieved using lists of references). In Fortran, multidimensional arrays are stored as a contiguous series of bytes, so you can pass in a single dimensional array to a Fortran function or subroutine assuming it has sufficient number of values.

Platypus updates any values that have been changed by Fortran when the Fortran code returns.

One thing to keep in mind is that Fortran arrays are "column-first", which is the opposite of C/C++, which could be termed "row-first".

Variable-length array

Fortran

function sum_array(size,a) result(ret)
  implicit none
  integer :: size
  integer, dimension(size) :: a
  integer :: i
  integer :: ret
 
  ret = 0
  
  do i=1,size
    ret = ret + a(i)
  end do
  
end function sum_array

Perl

use FFI::Platypus 2.00;

my $ffi = FFI::Platypus->new(
  api  => 2,
  lang => 'Fortran',
  lib  => './var_array.so',
);

$ffi->attach( sum_array => ['integer*','integer[]'] => 'integer',
  sub {
    my $f = shift;
    my $size = scalar @_;
    $f->(\$size, \@_);
  }
);

my @a = (1..10);
my @b = (25..30);

print sum_array(@a), "\n";
print sum_array(@b), "\n";

Execute

$ gfortran -shared var_array.f90 -o var_array.so
$ perl var_array.pl
55
165

Discussion

Fortran allows variable-length arrays. To indicate a variable length array use the [] notation without a length. Note that this works for argument types, where Perl knows the length of an array, but it will not work for return types, where Perl has no way of determining the size of the returned array (you can probably fake it with an opaque type and a wrapper function though).

METHODS

Generally you will not use this class directly, instead interacting with the FFI::Platypus instance. However, the public methods used by Platypus are documented here.

native_type_map

my $hashref = FFI::Platypus::Lang::Fortran->native_type_map;

This returns a hash reference containing the native aliases for Fortran. That is the keys are native Fortran types and the values are libffi native types.

mangler

my $mangler = FFI::Platypus::Lang::Fortran->mangler($ffi->libs);
my $c_name = $mangler->($fortran_name);

Returns a subroutine reference that will "mangle" Fortran names.

SUPPORT

If something does not work as advertised, or the way that you think it should, or if you have a feature request, please open an issue on this project's GitHub issue tracker:

https://github.com/plicease/FFI-Platypus-Lang-Fortran/issues

CONTRIBUTING

If you have implemented a new feature or fixed a bug then you may make a pull request on this project's GitHub repository:

https://github.com/plicease/FFI-Platypus-Lang-Fortran/pulls

Also Feel free to use the issue tracker:

https://github.com/plicease/FFI-Platypus-Lang-Fortran/issues

This project's GitHub issue tracker listed above is not Write-Only. If you want to contribute then feel free to browse through the existing issues and see if there is something you feel you might be good at and take a whack at the problem. I frequently open issues myself that I hope will be accomplished by someone in the future but do not have time to immediately implement myself.

Another good area to help out in is documentation. I try to make sure that there is good document coverage, that is there should be documentation describing all the public features and warnings about common pitfalls, but an outsider's or alternate view point on such things would be welcome; if you see something confusing or lacks sufficient detail I encourage documentation only pull requests to improve things.

Caution: if you do this too frequently I may nominate you as the new maintainer. Extreme caution: if you like that sort of thing.

CAVEATS

Fortran is pass by reference, which means that you need to pass pointers. Confusingly Platypus uses a star (*) suffix to indicate a pointer, and Fortran uses a star to indicate the size of types.

SEE ALSO

FFI::Platypus

The Core Platypus documentation.

FFI::Build + FFI::Build::File::Fortran

Bundle Fortran with your FFI / Perl extension.

AUTHOR

Author: Graham Ollis <plicease@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2015-2022 by Graham Ollis.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.