Skip to content
Documentaton and tools for using Platypus with Fortran
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
corpus
examples
inc
lib
maint
t
.gitattributes
.gitignore
.travis.yml
Build.PL
CONTRIBUTING
Changes
INSTALL
LICENSE
MANIFEST
MANIFEST.SKIP
META.json
META.yml
README
SUPPORT

README

NAME

    FFI::Platypus::Lang::Fortran - Documentation and tools for using
    Platypus with Fortran

SYNOPSIS

    Fortran 77:

     C Fortran function that adds two numbers together
     C On Linux create a .so with: gfortran -shared -o libadd.so add.f
           FUNCTION ADD(IA, IB)
               ADD = IA + IB
           END

    Fortran 90/95:

     ! Fortran function that adds two numbers together
     ! On Linux create a .so with: gfortran -shared -o libadd.so add.f90
     function add(a,b) result(ret)
       implicit none
       integer :: a
       integer :: b
       integer :: ret
       ret = a + b
     end function add

    Perl:

     use FFI::Platypus;
     $ffi->lang('Fortran');
     $ffi->lib('./libadd.so'); # or add.dll on Windows
     
     # Fortran is pass by reference, so use pointers
     $ffi->attach( add => [ 'integer*', 'integer*' ] => 'integer' );
     
     # Use a reference to an integer to pass
     # a pointer to an integer
     print add(\1,\2), "\n";  # prints 3

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

    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

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.

    This module currently uses and is bundled with a fork of ExtUtils::F77
    called Module::Build::FFI::Fortran::ExtUtilsF77. It is used to probe
    for a Fortran compiler, which can be problematic if you want to bundle
    Fortran 90 or Fortran 95 code, as it only knows about Fortran 77. On
    some platforms (such as those using gfortran) the same command is
    invoked to build all versions of fortran. On some (usually those with a
    f77 command) a f90 or f95 command is required to build code for newer
    versions of Fortran. We attempt to work around these limitations.

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.

EXAMPLES

 Call a subroutine

    Fortran:

     C Compile with gfortran -shared -o libsub.so sub.f
           SUBROUTINE ADD(IRESULT, IA, IB)
               IRESULT = IA + IB
           END

    Perl:

     use FFI::Platypus;
     
     my $ffi = FFI::Platypus->new;
     $ffi->lang('Fortran');
     $ffi->lib('./libsub.so');
     
     $ffi->attach( add => ['integer*','integer*','integer*'] => 'void');
     
     my $value = 0;
     add(\$value, \1, \2);
     
     print "$value\n";

    Discussion: A Fortran "subroutine" is just a function that doesn't
    return a value. In Fortran 77 variables that start wit the letter I are
    integers unless declared otherwise. Fortran is also pass by reference,
    which means 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
    in Perl in cases where you would normally pass in a simple value to a C
    function.

 Call Fortran 90 / 95

    Fortran:

     ! on Linux: gfortran -shared -fPIC -o libfib.so fib.f90
     
     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;
     
     my $ffi = FFI::Platypus->new;
     $ffi->lang('Fortran');
     $ffi->lib('./libfib.so');
     
     $ffi->attach( fib => ['integer*'] => 'integer' );
     
     for(1..10)
     {
       print fib(\$_), "\n";
     }

    Discussion: Fortran 90 has "advanced" features such as recursion and
    pointers, which can now be used in Perl too.

 Complex numbers

    Fortran:

     ! on Linux: gfortran -shared -fPIC -o libcomplex.so complex.f90
     
     subroutine complex_decompose(c,r,i)
       implicit none
       complex*16 :: c
       real*8 :: r
       real*8 :: i
       
       r = real(c)
       i = aimag(c)
     
     end subroutine complex_decompose

    Perl:

     use FFI::Platypus;
     use Math::Complex;
     
     my $ffi->lang('Fortran');
     my $ffi->lib('./libcomplex.so');
     
     $ffi->attach(
       complex_decompose => ['real_8[2]','real_8*','real_8*'] => 'void',
       sub {
         # wrapper around the Fortran function complex_decompose
         # $decompose is a code ref to the real complex_decompose
         # and $complex is the first argument passed int othe Perl
         # function complex_decompose
         my($decompose, $complex) = @_;
         my $real;
         my $imaginary;
         # decompose the Perl complex number and pass it as a
         # Fortran complex number
         $decompose->([Re($complex),Im($complex)], \$real, \$imaginary);
         # The decomposed real and imaginary parts are returned from
         # Fortran.  We pass them back to the caller as a return value
         ($real, $imaginary);
       },
     );
     
     my($r,$i) = complex_decompose(1.5 + 2.5*i);
     
     print "${r} + ${i}i\n";

    Discussion: More recent versions of libffi support complex types, but
    they are not supported by FFI::Platypus yet. There is a work around,
    however, at least for complex types passes as arguments. They are
    really two just two real*4 or real*8 types joined together like an
    array or record of two elements. Thus we can pass in a complex type to
    a Fortran subroutine as an array of two floating points. Take care
    though, as this technique DOES NOT work for return types.

    From my research, some Fortran compilers pass in the return address of
    the return value as the first argument for functions that return a
    complex type. This is not the case for Gnu Fortran, the compiler that I
    have been testing with, but if your compiler does use this convention
    you could pass in the "return value" as a two element array, as we did
    in the above example. I have not been able to test this though.

 Fixed length array

    Fortran:

     ! on Linux: gfortran -shared -fPIC -o libfixed.so fixed.f90
     
     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;
     
     my $ffi = FFI::Platypus->new;
     $ffi->lang('Fortran');
     $ffi->lib('./libfixed.so');
     
     $ffi->attach( print_array10  => ['integer[10]'] => 'void' );
     my $array = [5,10,15,20,25,30,35,40,45,50];
     print_array10($array);

    Output:

                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:

     ! On Linux gfortran -shared -fPIC -o libfixed2.so fixed2.f90
     
     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;
     
     my $ffi = FFI::Platypus->new;
     $ffi->lang('Fortran');
     $ffi->lib('./libfixed.so');
     
     $ffi->attach( print_array2x5 => ['integer[10]'] => 'void' );
     my $array = [5,10,15,20,25,30,35,40,45,50];
     print_array2x5($array);

    Output:

                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 sufficent 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:

     ! On Linux gfortran -shared -fPIC -o libvar.so var.f90
     
     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;
     
     my $ffi = FFI::Platypus->new;
     $ffi->lang("Fortran");
     $ffi->lib("./libvar_array.so");
     
     sub sum_array
     {
       my $size = @_;
       $ffi->function(
         sum_array => ['integer*',"integer[$size]"] => 'integer'
       )->call(\$size, \@_);
     }
     
     my @a = (1..10);
     my @b = (25..30);
    
     print sum_array(1..10), "\n";
     print sum_array(25..30), "\n";

    Output:

     55
     165

    Discussion: Fortran allows variable-length arrays. Unfortunately
    Platypus does not yet support this type. In this example, we work
    around this by creating an FFI function object with the correct type of
    fixed length array and call that. The downside to this is that it is
    quite slow, but it does allow you to do it at least.

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

SEE ALSO

    FFI::Platypus

      The Core Platypus documentation.

    Module::Build::FFI::Fortran

      Bundle Fortran with your FFI / Perl extension.

AUTHOR

    Graham Ollis <plicease@cpan.org>

COPYRIGHT AND LICENSE

    This software is copyright (c) 2015 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.

    This software comes bundled with a forked version of ExtUtils::F77
    called Module::Build::FFI::Fortran::ExtUtilsF77. ExtUtils::F77 comes
    with this statement regarding its license:

      Copyright (c) 2001 by Karl Glazebrook. All rights reserved.  This distribution 
      is free software; you can redistribute it and/or modify it under the same 
      terms as Perl itself.
    
      THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
      OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
      WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      ARE DISCLAIMED.  IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
      DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
      STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
      IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      POSSIBILITY OF SUCH DAMAGE.
    
      BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
      FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
      OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
      PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
      EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
      WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
      THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS
      WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
      ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
    
      IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
      WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
      REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR
      DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL
      DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM
      (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
      INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF
      THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER
      OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

You can’t perform that action at this time.