我应该把 'bind(C)' 放在 C 端没有定义的子程序旁边吗?

Should I put 'bind(C)' next to the subroutine which is not defined in C side?

比如说,我有一个必须在 Fortran 库中实现的 C++ 函数。

我决定在一个模块中制作我的 Fortran 包装器,如下所示。

(C++ 端)

extern "C" void cpp_func1(int* n, int* array) { ... }

(Fortran 方面)

module cpp2fortran
  use ISO_C_BINDING
  implicit none

  interface
! (1)
    subroutine cpp_func1(n, array) bind(C)
      use iso_c_binding
      integer(C_INT)     , intent(in) :: n
      type(C_PTR) , value, intent(in) :: array
    end subroutine
  end interface  

contains

! (2)
    subroutine Utilize_cpp_func1( ... )  ! bind(C)
    use iso_c_binding

    ...

    call cpp_func1(...)

    ...

  end subroutine

end module

我在两个地方标记了数字,例如(1) 和 (2).

在(1)的末尾,我肯定放了bind(C),没有它代码编译不通过。

问题是,我应该把 bind(C) 放在 (2) 的末尾吗?

代码 compiles/works 与行 (2) 末尾 bind(C) 的存在无关,但我发现如果我将 bind(C).

例如,在没有 bind(C)

的普通 Fortran 中
real, allocatable :: arr1(:)
real, allocatable :: arr2(:)

...

arr2 = arr1

...

可以正常工作。但是,如果我输入 bind(C),它不起作用,我必须进行一些修改,例如以下

real, allocatable :: arr1(:)
real, allocatable :: arr2(:)

...

do i = 1, size
  arr2(i) = arr1(i)
enddo

...

如果从 (2)-like Fortran 包装器中删除 bind(C) 是安全的,我很乐意将其删除并利用 arr2 = arr1 等简洁的优化,而不是编写冗长的代码做循环。


这是bind(C)出问题的最小例子。我是用ifort编译的

module cbind
  use iso_c_binding
  implicit none
contains
(*) subroutine sub1(array) !bind(C)
    use iso_c_binding
    implicit none
    real, allocatable, intent(in) :: array(:)

    real(C_FLOAT), allocatable, target :: array_target(:)

    allocate( array_target(size(array,1)) )

    array_target = array

    print *, 'array'
    print *, array
    print *, 'array_target'
    print *, array_target

  end subroutine
end module

program test
  use iso_c_binding
  use cbind
  implicit none
  real, allocatable :: array(:)

  allocate( array(5) )

  array = 1.0

  call sub1(array)

end program

如果 (*) 行没有 bind(C),代码就可以正常运行所需的输出。

array
  1.000000       1.000000       1.000000       1.000000       1.000000  
array_target
  1.000000       1.000000       1.000000       1.000000       1.000000  

但是,如果我输入 bind(C),代码会出现以下输出的段错误。

 array
   1.000000       1.000000       1.000000       1.000000       1.000000    
 array_target
forrtl: severe (174): SIGSEGV, segmentation fault occurred
Image              PC                Routine            Line        Source   
cbinding1          00000000004051B3  Unknown               Unknown  Unknown
libpthread-2.17.s  00002AC983A5E5F0  Unknown               Unknown  Unknown
cbinding1          000000000045382A  Unknown               Unknown  Unknown
cbinding1          000000000042B967  Unknown               Unknown  Unknown
cbinding1          000000000040FCB3  Unknown               Unknown  Unknown
cbinding1          000000000040CA01  Unknown               Unknown  Unknown
cbinding1          0000000000403C1B  Unknown               Unknown  Unknown
cbinding1          00000000004037E2  Unknown               Unknown  Unknown
libc-2.17.so       00002AC983C8D505  __libc_start_main     Unknown  Unknown
cbinding1          00000000004036E9  Unknown               Unknown  Unknown

过程的 BIND 属性对于使其可与 C 语言互操作是必需的。尽管它还有其他作用,但对于 Fortran 过程本身而言,该属性并不是必需的,它也可以使用可互操作的过程。更具体地说,不可互操作的过程可能会使用 non-interoperable 过程:人们可能会经常使用这个非常有用的事实。

在你的情况下(据我们所见),你不需要 bind(c) 定义 Utilize_cpp_func1。此过程本身不需要与 C 互操作。

关于 array_target = array 的问题,这只是某些版本的英特尔编译器中的一个编译器弱点。当前版本中不存在。

为什么 bind(c) 在这个例子中有所不同?您的虚拟参数 array 是可分配的。可互操作的过程可能仅在 Fortran 2008+TS29113 或 Fortran 2018 下有一个可分配的虚拟参数,在 Fortran 2003 或 Fortran 2008 下没有:它是该语言的一个相对较新的特性,并且容易在其实现中(或使用时)出现一些错误不允许此功能的语言解释)。