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