If you are doing heavy scientific computing, you will eventually encounter Fortran (BLAS, LAPACK).

Binding Fortran can be tricky because:

  1. Name Mangling: Compilers often lowercase names and append underscores (e.g., DGEMM becomes dgemm_).
  2. Pass-by-Reference: Historically, Fortran passes everything by reference (pointers).
  3. Strings: Legacy Fortran expects string pointers to be followed by hidden length arguments at the end of the argument list.

However, modern Fortran (2003+) offers the iso_c_binding module, which allows us to define a standard C ABI.

The Recipe

This time, we'll compile a Fortran subroutine that adds two numbers, and another that prints a string based on an explicit length argument.

use v5.40;
use Affix qw[:all];
use Affix::Build;
$|++;

# 1. Compile Fortran
# Requires a Fortran compiler be installed
my $c = Affix::Build->new( name => 'fortran_demo' );
$c->add( \<<~'END', lang => 'f90' );
        ! Numeric subroutine: Passes pointers by default
        subroutine f_add(a, b, res) bind(c, name='f_add')
            use iso_c_binding
            integer(c_int), intent(in) :: a, b
            integer(c_int), intent(out) :: res
            res = a + b
        end subroutine

        ! String subroutine: Explicit length handling
        subroutine f_hello(msg, len) bind(c, name='f_hello')
            use iso_c_binding
            character(kind=c_char, len=1), intent(in) :: msg(*)
            integer(c_int), value :: len

            ! Print the slice using the passed length
            print *, "Fortran says: ", msg(1:len)
        end subroutine
    END
my $lib = $c->link;

# 2. Bind
# Modern Fortran with bind(c) respects C names.
# However, Fortran arguments are usually pointers.
affix $lib, 'f_add', [ Pointer [Int], Pointer [Int], Pointer [Int] ] => Void;

# For strings, we pass the buffer (String) and the length (Int) explicitly.
affix $lib, 'f_hello', [ String, Int ] => Void;

# 3. Call (Math)
# We must pass references (\$) to our numbers because Fortran expects pointers.
my $a   = 10;
my $b   = 20;
my $res = 0;
f_add( \$a, \$b, \$res );
say "10 + 20 = $res";    # 30

# 4. Call (Strings)
my $msg = 'Hello World';

# We pass the string string (char*), AND the length manually.
f_hello( $msg, length($msg) );

How It Works

Kitchen Reminders