๐ŸŒ‰ Foreign Function Interfaces

Foreign Function Interfaces (FFIs) in Python allow Python code to call C libraries directly. This capability is useful if you want to use legacy C code, optimise performance-critical sections of an application, or use hardware-accelerated or system-level functionality not directly available in Python. The most common tools for working with FFIs in Python are ctypes and cffi.

ctypes

ctypes is a foreign function library for Python that provides C compatible data types and allows calling functions in DLLs or shared libraries. It can be used to wrap these libraries in pure Python.

Here's a simple example that uses ctypes to call the time function from the C standard library, which returns the current time in seconds since the Epoch (1970-01-01 00:00:00 +0000 (UTC)):

import ctypes
# Load the C standard library
libc = ctypes.CDLL(None)
# Define the return type of the function we are going to call
libc.time.argtypes = [ctypes.POINTER(ctypes.c_long)]
libc.time.restype = ctypes.c_long

t = libc.time(None)
print(f"The current time is {t} seconds since the Epoch.")

cffi

cffi (C Foreign Function Interface) is another library for calling C code from Python. Compared to ctypes, cffi provides more advanced features like out-of-line API mode, which allows for better error checking and integration with existing C code.

Below is an example of using cffi to achieve the same functionality as the ctypes example, calling the time function from the C library.

First, you need to install cffi: pip install cffi

Then, you can use cffi like this:

from cffi import FFI
ffi = FFI()
# Define the external C function
ffi.cdef("long time(long *t);")
# Load the C standard library
C = ffi.dlopen(None)
t = ffi.new("long *")
print(f"The current time is {C.time(t)} seconds since the Epoch.")

ctypes vs cffi

ctypes is part of the standard Python library, so it doesn't require any additional installations. It's straightforward for simple use cases but can become cumbersome for complex C libraries or where callback functions are involved.

cffi requires installation but offers a more flexible and powerful interface for working with C code. It supports both ABI (Application Binary Interface) level and API (Application Programming Interface) level interfaces, making it suitable for more complex integration scenarios.

The choice between them ultimately depends on the specific requirements of the project, such as the complexity of the C code being interfaced and the performance requirements.

What About Fortran?

If you have legacy scientific codes, the most common way to bridge Python and Fortran is through the use of f2py or NumPyโ€™s integration facilities.

f2py is one of the easiest ways to call Fortran code from Python. It generates Python wrapper modules automatically, allowing Fortran routines to be called as if they were Python functions.

Suppose you have a simple Fortran subroutine that calculates the sum of two arrays:

subroutine add_arrays(a, b, c, n)
    integer, intent(in) :: n
    double precision, intent(in) :: a(n), b(n)
    double precision, intent(out) :: c(n)
    integer :: i
    do i = 1, n
        c(i) = a(i) + b(i)
    end do
end subroutine add_arrays

You can compile this Fortran code into a Python module using f2py:

f2py -c -m addarrays addarrays.f90

This command creates a Python module named addarrays. You can then import this module in Python and call the add_arrays function:

import addarrays
import numpy as np

a = np.array([1.0, 2.0, 3.0], dtype=np.float64)
b = np.array([4.0, 5.0, 6.0], dtype=np.float64)
c = addarrays.add_arrays(a, b)

print(c)

Get PDF