Writing a Fortran-C interface

tags: book

The reason why Fortran can be called from Python is the C interoperability features that were added with Fortran 2003 and expanded later. By writing our code to be interoperable with C, we can call procedures, module variables and derived types from C code. Many other programming languages, such as Python, then gain access to our Fortran code, as they are able to call C code.

C interoperable code

The two most important parts of making your Fortran code C interoperable are:

  1. Using the bind(c) attribute
  2. Using C interoperable types

bind(c) is built in, while the C interoperable types are available through the intrinsic module iso_c_binding that needs to be imported. We have seen one basic example of this in the Introduction:

module mod_add
  use, intrinsic :: iso_c_binding, only: c_int
  implicit none

  contains
    integer(c_int) function add(a, b) bind(c)
      integer(c_int), value, intent(in) :: a, b
      add = a + b
    end function add
end module mod_add

Another key difference between C and Fortran is that variables are generally passed by value rather than reference, unless otherwise specified. For example, a C function declaration can look like this:

void sum_columns(double *a, int n_r_a, int n_c_a, double *b);

Here, a is of type real with double precision, that is passed by reference. That means that what is passed is a pointer to another variable. It could be a scalar, or an array. n_r_a on the other hand, is passed by value, and a copy is made that is used by the function. The attribute value can be added to the Fortran variable declaration to indicate that the variable is passed by value as in C standard.

There are many C types available from iso_c_binding, but here are the most important ones, and their corresponding types both in C, Python and NumPy.

Insert table

Handling strings

Wrapper code

It might not be convenient to write all code in a C interoperable manner (it also comes with some limitations). You might also be working with legacy code that would be very time consuming to refactor. In addition, usually you would only be interested in using specific parts of the code from Python. The pragmatic solution in most cases is to write your codebase with Fortran types, and then write a wrapper that exposes the key functionality to C. This approach works well both for new and legacy code.