Reputation: 71
I'm currently working on wrapping a Fortran module to be called from Python using Cython, the Fortran subroutine currently has an optional logical argument. I've used iso_c_binding to wrap the subroutine, and I have written the cython .pyx file to create the .so file. However, when I run my setup script, I get two warnings:
warning: passing argument 4 of 'c_geo_manipulate' from incompatible pointer type [-Wincompatible-pointer-types]
c_geo_manipulate(((geo *)(&__pyx_v_v1)), ((geo *)(&__pyx_v_v2)), ((geo *)(&__pyx_v_value)), ((int *)(&__pyx_v_optional_weights_output)));
And
pyginterpolation.h:17:13: note: expected '_Bool *' but argument is of type 'int *'
extern void c_geo_manipulate(geo *v1, geo *v2, geo *v3, bool *output_weight);
Here are the files I'm using:
interpolation.f90
module interpolation_module
use iso_c_binding
implicit none
type, bind(c) :: geo
real, dimension(2) :: coordinates
real :: weight
end type geo
contains
subroutine geo_manipulate(v1, v2, v3, output_weight)
type(geo), intent(in) :: v1, v2
logical, optional, intent(in) :: output_weight
type(geo), intent(out) :: v3
if (present(output_weight).and.(output_weight .eqv. .true.)) then
v3%coordinates = v1%coordinates * v1%weight + v2%coordinates * v2%weight
v3%weight = v1%weight + v2%weight
else
print *, 'false'
v3%coordinates = v1%coordinates * v1%weight + v2%coordinates * v2%weight
v3%weight = 0
end if
end subroutine geo_manipulate
end module interpolation_module
pyginterpolation.f90
module interpolation_interface
use interpolation_module
use iso_c_binding
implicit none
contains
subroutine c_geo_manipulate(v1, v2, v3, output_weight) bind(c)
type(geo), intent(in) :: v1, v2
type(geo), intent(out) :: v3
logical(c_bool), optional, intent(in) :: output_weight
if ((present(output_weight)).and.(output_weight .eqv. .true.)) then
call geo_manipulate(v1, v2, v3, output_weight = .true.)
else
call geo_manipulate(v1, v2, v3)
end if
end subroutine c_geo_manipulate
end module interpolation_interface
pyginterpolation.h
#include <stdbool.h>
struct _geo {
float coordinates[2];
float weight;
};
typedef struct _geo geo;
extern void c_geo_manipulate(geo *v1, geo *v2, geo *v3, bool *output_weight);
pyginterpolation.pyx
import numpy as np
cdef bint boolean_variable = True
cdef extern from "pyginterpolation.h":
ctypedef struct geo:
float coordinates[2]
float weight
cdef void c_geo_manipulate(geo *v1, geo *v2, geo *v3, bint *output_weight)
def f(geo v1, geo v2, output_weights = False):
cdef geo value
if output_weights is True:
optional_weights_output = True
c_geo_manipulate(<geo*> &v1,<geo*> &v2,<geo*> &value, <bint*> &optional_weights_output)
return np.array(value)
else:
optional_weights_output = False
c_geo_manipulate(<geo*> &v1,<geo*> &v2,<geo*> &value, <bint*> &optional_weights_output)
return np.array(value.coordinates)
setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
# This line only needed if building with NumPy in Cython file.
from numpy import get_include
from os import system
# compile the fortran modules without linking
fortran_mod_comp = 'gfortran interpolation.f90 -c -o interpolation.o -O3 -fPIC'
print (fortran_mod_comp)
system(fortran_mod_comp)
shared_obj_comp = 'gfortran pyginterpolation.f90 -c -o pyginterpolation.o -O3 -fPIC'
print (shared_obj_comp)
system(shared_obj_comp)
ext_modules = [Extension(# module name:
'pyginterpolation',
# source file:
['pyginterpolation.pyx'],
# other compile args for gcc
extra_compile_args=['-fPIC', '-O3'],
# other files to link to
extra_link_args=['interpolation.o', 'pyginterpolation.o'])]
setup(name = 'pyginterpolation',
cmdclass = {'build_ext': build_ext},
# Needed if building with NumPy.
# This includes the NumPy headers when compiling.
include_dirs = [get_include()],
ext_modules = ext_modules)
From what I've understood, cython has bint to be used for booleans with C, but I'm not too comfortable with how they work,and I'm still very new to C/Fortran, so my understanding of how pointers (and the language in general) work is still pretty rudimentary. Does anyone have an idea what might be wrong with my usage of these logicals?
Thanks!
Upvotes: 4
Views: 145
Reputation: 37198
The Cython bint
type does not map Python booleans to the C99 _Bool
type, but rather to int
. So the type of the output_weight argument should be integer(c_int)
. That is, something like
subroutine c_geo_manipulate(v1, v2, v3, output_weight) bind(c)
type(geo), intent(in) :: v1, v2
type(geo), intent(out) :: v3
logical(c_int), optional, intent(in) :: output_weight
logical :: ow
if ((present(output_weight)).and.(output_weight == 0)) then
ow = .FALSE.
else
ow = .TRUE.
end if
if (ow) then
call geo_manipulate(v1, v2, v3, ow)
else
call geo_manipulate(v1, v2, v3)
end if
end subroutine c_geo_manipulate
Upvotes: 3