如何使用 iso_c_binding 传递指向变量的指针?

问题描述

我有以下 C 库(特别是我现在正在调试的函数pass_by_reference):

#include <stdlib.h>

double multiply_numbers(double a,double b);
double *get_array(double a);
void pass_by_reference(int *a);

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

double *get_array(double a) {
    double *retval;
    int i;

    retval = malloc(100*sizeof(a));

    for(i=0; i<100; i++) {
        retval[i] = i*a;
    }

    return retval;
}

void pass_by_reference(int *a) {
    *a = 8; 
}

我正试图用一个名为 pass_by_reference 的 Fortran 子程序来包装它(后者将在 python 中通过 f2py 调用):

module test_c_lib

        use iso_c_binding
        implicit none
        
contains

        subroutine multiply(x,y,z)

                use iso_c_binding

                real(8),intent(in)        :: x
                real(8),intent(in)        :: y
                real(8),intent(out)       :: z
        
                ! Interface to C function
                interface                        
                        real(c_double) function c_multiply_numbers(a,b) bind(C,name="multiply_numbers")
                                import
                                real(c_double),value :: a,b
                        end function
                end interface

                ! Call C function               
                z = c_multiply_numbers(x,y)                

        end subroutine
        
        subroutine get_array(x,intent(out)       :: z(100)
                
                type(c_ptr)   :: ret_c_ptr
                real(8),pointer :: f_ptr(:)
        
                ! Interface to C function
                interface                        
                        type(c_ptr) function c_get_array(a) bind(C,name="get_array")
                                import
                                real(c_double),value :: a
                        end function
                end interface

                ! Call C function               
                ret_c_ptr = c_get_array(x)                
                call c_f_pointer(ret_c_ptr,f_ptr,[100])
                               
                z = f_ptr

        end subroutine
        
        subroutine pass_by_reference(z)

                use iso_c_binding

                integer,intent(out)       :: z
                
                ! Interface to C function
                interface                        
                        type(c_null_ptr) function c_pass_by_reference(a) bind(C,name="pass_by_reference")
                                import
                                type(c_int),value :: a
                        end function
                end interface

                ! Call C function                
                c_pass_by_reference(z)

        
        end subroutine


end module

以及对应的makefile:

$ cat makefile 
f_mod.so:       f_mod.f90 c_lib.o
                f2py -c f_mod.f90 c_lib.o -m f_mod

c_lib.o:        c_lib.c
                gcc -c -fpic c_lib.c -o c_lib.o

尝试使用 f2py 编译时,我得到:

   57 |                 use iso_c_binding
      |                    2                    
......
   63 |                         type(c_null_ptr) function c_pass_by_reference(a) bind(C,name="pass_by_reference")
      |                                        1
Error: Type name 'c_null_ptr' at (1) conflicts with prevIoUsly declared entity at (2),which has the same name
f_mod.f90:65:43:

   57 |                 use iso_c_binding
      |                    2                       
......
   65 |                                 type(c_int),value :: a
      |                                           1
Error: Type name 'c_int' at (1) conflicts with prevIoUsly declared entity at (2),which has the same name
f_mod.f90:63:24:

   63 |                         type(c_null_ptr) function c_pass_by_reference(a) bind(C,name="pass_by_reference")
      |                        1
Error: The type for function 'c_pass_by_reference' at (1) is not accessible
f_mod.f90:63:24: Warning: Implicitly declared BIND(C) variable 'c_pass_by_reference' at (1) may not be C interoperable [-Wc-binding-type]
f_mod.f90:70:35:

   70 |                 c_pass_by_reference(z)
      |                                   1
Error: 'c_pass_by_reference' at (1) is not a variable
f_mod.f90:63:24-71:

   63 |                         type(c_null_ptr) function c_pass_by_reference(a) bind(C,name="pass_by_reference")
      |                        2                                              1
Warning: Implicitly declared variable 'a' at (1) may not be C interoperable but it is a dummy argument to the BIND(C) procedure 'c_pass_by_reference' at (2) [-Wc-binding-type]
f_mod.f90:63:24:

   63 |                         type(c_null_ptr) function c_pass_by_reference(a) bind(C,name="pass_by_reference")
      |                        1
Warning: Implicitly declared BIND(C) function 'c_pass_by_reference' at (1) may not be C interoperable [-Wc-binding-type]
error: Command "/usr/local/bin/gfortran9 -Wall -g -fno-second-underscore -fPIC -O3 -funroll-loops -I/tmp/tmpk7tn1bgi/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 -fPIC f_mod.f90 -o /tmp/tmpk7tn1bgi/f_mod.o -J/tmp/tmpk7tn1bgi/ -I/tmp/tmpk7tn1bgi/" Failed with exit status 1
*** Error code 1

我有两个问题:

  • 当使用 iso_c_binding 时,在此特定示例中,传递给 a 函数的变量 pass_by_reference 是实际通过引用传递还是我必须在界面中指定不同的内容
  • 为什么 f2py 报告 Type name 'c_int' at (1) conflicts with prevIoUsly declared entity at (2),which has the same name
  • 如何指定 C 函数不返回任何内容 (void)?我曾尝试使用 type(c_null_ptr)

解决方法

在执行评论中指出的所有更正后,这是有效的封装:

module test_c_lib

        use iso_c_binding
        implicit none
        
contains

        subroutine multiply(x,y,z)

                use iso_c_binding

                real(8),intent(in)        :: x
                real(8),intent(in)        :: y
                real(8),intent(out)       :: z
        
                ! Interface to C function
                interface                        
                        real(c_double) function c_multiply_numbers(a,b) bind(C,name="multiply_numbers")
                                import
                                real(c_double),value :: a,b
                        end function
                end interface

                ! Call C function               
                z = c_multiply_numbers(x,y)                

        end subroutine
        
        subroutine get_array(x,intent(out)       :: z(100)
                
                type(c_ptr)   :: ret_c_ptr
                real(8),pointer :: f_ptr(:)
        
                ! Interface to C function
                interface                        
                        type(c_ptr) function c_get_array(a) bind(C,name="get_array")
                                import
                                real(c_double),value :: a
                        end function
                end interface

                ! Call C function               
                ret_c_ptr = c_get_array(x)                
                call c_f_pointer(ret_c_ptr,f_ptr,[100])
                               
                z = f_ptr

        end subroutine
        
        subroutine pass_by_reference(z)

                use iso_c_binding

                integer,intent(out)            :: z
                
                ! Interface to C function
                interface                        
                        subroutine c_pass_by_reference(a) bind(C,name="pass_by_reference")
                                import
                                integer(c_int) :: a
                        end subroutine
                end interface

                ! Call C function                
                call c_pass_by_reference(z)

        
        end subroutine


end module

JupyterNotebookOutput