cffi

This example interacts with the shared object file fortran/example.so. Similar to ctypes, the cffi library enables the creation of a foreign function interface (FFI) via dlopen:

>>> import cffi
>>> ffi = cffi.FFI()
>>> so_file = "fortran/example.so"
>>> lib_example = ffi.dlopen(so_file)
>>> lib_example
<cffi.api._make_ffi_library.<locals>.FFILibrary object at 0x7fdd0e364ba8>

After dynamically loading the path, we need to manually define each member of the ABI that we’ll use (both the functions and the structs):

ffi.cdef("void foo(double bar, double baz, double *quux);")
ffi.cdef(
    "typedef struct UserDefined {\n"
    "  double buzz;\n"
    "  double broken;\n"
    "  int how_many;\n"
    "} UserDefined;"
)
ffi.cdef(
    "void make_udf(double *buzz, double *broken,\n"
    "              int *how_many, UserDefined *quux);"
)
ffi.cdef("void foo_array(int *size, double *val, double *two_val);")
ffi.cdef("void udf_ptr(intptr_t *ptr_as_int);")
ffi.cdef("void just_print();")
ffi.cdef("int __example_MOD_view_knob(void);")
ffi.cdef("void turn_knob(int *new_value);")

In order to convert a NumPy array to a type that can be used with cffi, we use the existing ctypes interface:

def numpy_pointer(array, ffi):
    if array.dtype != np.float64:
        raise TypeError("Unexpected data type", array.dtype)
    return ffi.cast("double *", array.ctypes.data)

Output

$ python python/check_cffi.py
------------------------------------------------------------
quux = foo(1.0, 16.0) = 61.0
------------------------------------------------------------
quuz = make_udf(1.25, 5.0, 1337)
     = UserDefined(1.25, 5.0, 1337)
------------------------------------------------------------
val =
[[ 3.    4.5 ]
 [ 1.    1.25]
 [ 9.    0.  ]
 [-1.    4.  ]]
two_val = foo_array(4, val)
two_val =
[[ 6.   9. ]
 [ 2.   2.5]
 [18.   0. ]
 [-2.   8. ]]
------------------------------------------------------------
ptr_as_int = address(made_it)  # intptr_t / ssize_t / long
ptr_as_int = 14735136  # 0xe0d720
udf_ptr(ptr_as_int)  # Set memory in ``made_it``
made_it = UserDefined(3.125, -10.5, 101)
------------------------------------------------------------
just_print()
 ======== BEGIN FORTRAN ========
 just_print() was called
 ========  END  FORTRAN ========
------------------------------------------------------------
view_knob() = 1337
turn_knob(42)
view_knob() = 42