RUDH
RUDH

Reputation: 71

Issues with interfacing Fortran Logicals with Cython Bint

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

Answers (1)

janneb
janneb

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

Related Questions