fortranf2pyfortran-iso-c-binding

Segmentation Fault when trying to write a Fortran wrapper around two C objects using f2py


I am experimenting with using f2py to encapsulate C code.

As a first test I have prepared two very basic C source files:

module1.c

double multiply(double a, double b);

double multiply(double a, double b) {
        return a*b;
}

module2.c

double divide(double a, double b);

double divide(double a, double b) {
        return a/b;
}

I compile both files as follows:

$ gcc -c -fPIC -o module1.o module1.c
$ gcc -c -fPIC -o module2.o module2.c

Then I prepare the following Fortran module wrapper for both functions:

fortran_module.f90

! TEST FORTRAN WRAPPER

module fortran_module

        use iso_c_binding

        ! Interface to C routine
        ! double get_price_step(double price);
        interface
                real(c_double) function multiply(a, b) bind(C)
                        use iso_c_binding
                        real(c_double), value :: a,b
                end function
                real(c_double) function divide(a, b) bind(C)
                        use iso_c_binding
                        real(c_double), value :: a,b
                end function
        end interface

end module

I compile the module using:

$ f2py -c -m fortran_module fortran_module.f90 module1.o module2.o

And I get the following output:

running build
running config_cc
unifing config_cc, config, build_clib, build_ext, build commands --compiler options
running config_fc
unifing config_fc, config, build_clib, build_ext, build commands --fcompiler options
running build_src
build_src
building extension "fortran_module" sources
f2py options: []
f2py:> /tmp/tmp9aa3w8b0/src.freebsd-12.2-RC1-amd64-3.7/fortran_modulemodule.c
creating /tmp/tmp9aa3w8b0/src.freebsd-12.2-RC1-amd64-3.7
Reading fortran codes...
        Reading file 'fortran_module.f90' (format:free)
Post-processing...
        Block: fortran_module
                        Block: fortran_module
In: :fortran_module:fortran_module.f90:fortran_module
get_useparameters: no module iso_c_binding info used by fortran_module
In: :fortran_module:fortran_module.f90:fortran_module:unknown_interface
get_useparameters: no module iso_c_binding info used by unknown_interface
                                        Block: multiply
In: :fortran_module:fortran_module.f90:fortran_module:unknown_interface:multiply
get_useparameters: no module iso_c_binding info used by multiply
                                        Block: divide
In: :fortran_module:fortran_module.f90:fortran_module:unknown_interface:divide
get_useparameters: no module iso_c_binding info used by divide
Post-processing (stage 2)...
        Block: fortran_module
                Block: unknown_interface
                        Block: fortran_module
                                Block: unknown_interface
                                        Block: multiply
                                        Block: divide
Building modules...
        Building module "fortran_module"...
                Constructing F90 module support for "fortran_module"...
Skipping interface unknown_interface
        Wrote C/API module "fortran_module" to file "/tmp/tmp9aa3w8b0/src.freebsd-12.2-RC1-amd64-3.7/fortran_modulemodule.c"
        Fortran 90 wrappers are saved to "/tmp/tmp9aa3w8b0/src.freebsd-12.2-RC1-amd64-3.7/fortran_module-f2pywrappers2.f90"
  adding '/tmp/tmp9aa3w8b0/src.freebsd-12.2-RC1-amd64-3.7/fortranobject.c' to sources.
  adding '/tmp/tmp9aa3w8b0/src.freebsd-12.2-RC1-amd64-3.7' to include_dirs.
copying /usr/local/lib/python3.7/site-packages/numpy/f2py/src/fortranobject.c -> /tmp/tmp9aa3w8b0/src.freebsd-12.2-RC1-amd64-3.7
copying /usr/local/lib/python3.7/site-packages/numpy/f2py/src/fortranobject.h -> /tmp/tmp9aa3w8b0/src.freebsd-12.2-RC1-amd64-3.7
  adding '/tmp/tmp9aa3w8b0/src.freebsd-12.2-RC1-amd64-3.7/fortran_module-f2pywrappers2.f90' to sources.
build_src: building npy-pkg config files
running build_ext
customize UnixCCompiler
C compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -O2 -pipe -fstack-protector-strong -fno-strict-aliasing -fPIC

creating /tmp/tmpjgjjhjtm/tmp
creating /tmp/tmpjgjjhjtm/tmp/tmpjgjjhjtm
compile options: '-MMD -MF /tmp/tmpjgjjhjtm/file.c.d -c'
cc: /tmp/tmpjgjjhjtm/file.c
customize UnixCCompiler using build_ext
get_default_fcompiler: matching types: '['gnu', 'gnu95']'
customize GnuFCompiler
Found executable /usr/local/bin/gfort

ran9
gnu: no Fortran 90 compiler found
gnu: no Fortran 90 compiler found
customize Gnu95FCompiler
customize Gnu95FCompiler
customize Gnu95FCompiler using build_ext
building 'fortran_module' extension
compiling C sources
C compiler: cc -pthread -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -O2 -pipe -fstack-protector-strong -fno-strict-aliasing -fPIC

creating /tmp/tmp9aa3w8b0/tmp
creating /tmp/tmp9aa3w8b0/tmp/tmp9aa3w8b0
creating /tmp/tmp9aa3w8b0/tmp/tmp9aa3w8b0/src.freebsd-12.2-RC1-amd64-3.7
compile options: '-I/tmp/tmp9aa3w8b0/src.freebsd-12.2-RC1-amd64-3.7 -I/usr/local/lib/python3.7/site-packages/numpy/core/include -I/usr/local/include/python3.7m -c'
cc: /tmp/tmp9aa3w8b0/src.freebsd-12.2-RC1-amd64-3.7/fortranobject.c
cc: /tmp/tmp9aa3w8b0/src.freebsd-12.2-RC1-amd64-3.7/fortran_modulemodule.c
In file included from /tmp/tmp9aa3w8b0/src.freebsd-12.2-RC1-amd64-3.7/fortran_modulemodule.c:15:
In file included from /tmp/tmp9aa3w8b0/src.freebsd-12.2-RC1-amd64-3.7/fortranobject.h:13:
In file included from /tmp/tmp9aa3w8b0/src.freebsd-12.2-RC1-amd64-3.7/fortranobject.c:2:
In file included from /tmp/tmp9aa3w8b0/src.freebsd-12.2-RC1-amd64-3.7/fortranobject.h:13:
In file included from /usr/local/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4:
In file included from /usr/local/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12:
In file included from /usr/local/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1822:
/usr/local/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: "Using deprecated NumPy API, disable it with "
      "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-W#warnings]
#warning "Using deprecated NumPy API, disable it with " \
 ^
In file included from /usr/local/lib/python3.7/site-packages/numpy/core/include/numpy/arrayobject.h:4:
In file included from /usr/local/lib/python3.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:12:
In file included from /usr/local/lib/python3.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1822:
/usr/local/lib/python3.7/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: "Using deprecated NumPy API, disable it with "
      "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-W#warnings]
#warning "Using deprecated NumPy API, disable it with " \
 ^
1 warning generated.
1 warning generated.
compiling Fortran 90 module sources
Fortran f77 compiler: /usr/local/bin/gfortran9 -Wall -g -ffixed-form -fno-second-underscore -fPIC -O3 -funroll-loops
Fortran f90 compiler: /usr/local/bin/gfortran9 -Wall -g -fno-second-underscore -fPIC -O3 -funroll-loops
Fortran fix compiler: /usr/local/bin/gfortran9 -Wall -g -ffixed-form -fno-second-underscore -Wall -g -fno-second-underscore -fPIC -O3 -funroll-loops
compile options: '-I/tmp/tmp9aa3w8b0/src.freebsd-12.2-RC1-amd64-3.7 -I/usr/local/lib/python3.7/site-packages/numpy/core/include -I/usr/local/include/python3.7m -c'
extra options: '-J/tmp/tmp9aa3w8b0/ -I/tmp/tmp9aa3w8b0/'
gfortran9:f90: fortran_module.f90
compiling Fortran sources
Fortran f77 compiler: /usr/local/bin/gfortran9 -Wall -g -ffixed-form -fno-second-underscore -fPIC -O3 -funroll-loops
Fortran f90 compiler: /usr/local/bin/gfortran9 -Wall -g -fno-second-underscore -fPIC -O3 -funroll-loops
Fortran fix compiler: /usr/local/bin/gfortran9 -Wall -g -ffixed-form -fno-second-underscore -Wall -g -fno-second-underscore -fPIC -O3 -funroll-loops
compile options: '-I/tmp/tmp9aa3w8b0/src.freebsd-12.2-RC1-amd64-3.7 -I/usr/local/lib/python3.7/site-packages/numpy/core/include -I/usr/local/include/python3.7m -c'
extra options: '-J/tmp/tmp9aa3w8b0/ -I/tmp/tmp9aa3w8b0/'
gfortran9:f90: /tmp/tmp9aa3w8b0/src.freebsd-12.2-RC1-amd64-3.7/fortran_module-f2pywrappers2.f90
/usr/local/bin/gfortran9 -Wall -g -Wall -g -shared /tmp/tmp9aa3w8b0/tmp/tmp9aa3w8b0/src.freebsd-12.2-RC1-amd64-3.7/fortran_modulemodule.o /tmp/tmp9aa3w8b0/tmp/tmp9aa3w8b0/src.freebsd-12.2-RC1-amd64-3.7/fortranobject.o /tmp/tmp9aa3w8b0/fortran_module.o /tmp/tmp9aa3w8b0/tmp/tmp9aa3w8b0/src.freebsd-12.2-RC1-amd64-3.7/fortran_module-f2pywrappers2.o module1.o module2.o -L/usr/local/lib/gcc9/gcc/x86_64-portbld-freebsd12.1/9.3.0 -L/usr/local/lib -lpython3.7m -lgfortran -o ./fortran_module.so
Removing build directory /tmp/tmp9aa3w8b0

This creates fortran_module.so in the directory.

Then, back in Python I try to do:

import fortran_module

But I get a segmentation fault:

$ /usr/local/bin/python3.7
Python 3.7.9 (default, Oct  3 2020, 01:29:35) 
[Clang 8.0.1 (tags/RELEASE_801/final 366581)] on freebsd12
Type "help", "copyright", "credits" or "license" for more information.
readline: ~/.inputrc: line 1: nobeep: unknown variable name
readline: ~/.inputrc: line 1: nobeep: unknown variable name
>>> import fortran_module
Segmentation fault (core dumped)

Any help or hint on what I am doing wrong is welcomed. I am using gcc and FreeBSD (I abandoned the idea of using clang anf flang as FreeBSD's flang seems not to support iso_c_bindings).

Note: I have also tried using --fcompiler=gnu95 as parameter of f2py trying to ensure that flang or clang are not used.


Solution

  • Assuming thatf2py cannot generate Python binding for Fortran routines with bind(C), a workaround might be to define a usual (non-bind(C)) routine and call C routines from inside (so somewhat similar to this page for using derived types). For example, a possible code may look like this (but clearly with more overhead for function calls...)

    clib.c

    double multiply(double a, double b) {
        return a * b;
    }
    

    fmod.f90

    module fmod
        use iso_c_binding
        implicit none
    contains
    
        function multiply(a, b) result(res)
            real(8) :: a, b, res   !! "8" just for test
            interface
                real(c_double) function c_multiply(a, b) bind(C,name="multiply")
                    import
                    real(c_double), value :: a,b
                end
            end interface
    
            res = c_multiply(a, b)
        end
    end module
    

    Compile:

    $ gcc -c -fPIC clib.c -o clib.o
    $ python3.9 -m numpy.f2py -c fmod.f90 clib.o -m py_fmod
    

    Run:

    $ python3.9
    >>> import py_fmod
    >>> print( py_fmod.fmod.__doc__ )
    res = multiply(a,b)
    
    Wrapper for ``multiply``.
    
    Parameters
    ----------
    a : input float
    b : input float
    
    Returns
    -------
    res : float
    
    >>> py_fmod.fmod.multiply( a= 2.0, b= 3.0 )
    6.0
    

    I guess it will also be possible to define all the interface blocks in a separate Fortran module file, compile it independently from f2py, and use it from routines compiled with f2py (so, separating the iso_c_binding + interface block from the f2py compilation). Indeed, it seems problematic if such an interface block is defined directly in the module header part for f2py (which seems very similar to the problem of derived types etc).