Fortran-C 互操作性和浮点数组

Fortran-C interoperability and float arrays

我有大量现有的 Fortran95 代码。它使用

real(dp), dimension(num) :: array

声明数组。

我想加入一些 C 代码,发现我可以通过编写 C 函数的接口并将数组声明为

来做到这一点
use iso_c_binding
real(c_double), allocatable, target :: array(:)

我有工作的 fortran 函数调用 C 函数

call myfunction(c_loc(array));

real(dp) 数组传递给 myfunction 需要什么?显然,我需要从中创建一个 C 指针(怎么做?)。除了复制数组还有其他方法吗?是否可以确保两种类型确实引用兼容的双精度数据块?最重要的是,该解决方案必须与 GNU 编译器一起工作。请注意,将现有 Fortran 代码中的 real(dp) 替换为 real(c_double) 现在对我来说不是一个选项。

如果除了复制整个数组别无选择,我该如何在界面中正确执行此操作?

首先,我假设您将 dp 定义为某处模块中的参数。你可以简单地使用

integer, parameter :: dp = c_double

在该模块中(并且在某处有 if (dp /= c_double) stop "Bletchful sytem"

在 C 和 Fortran 之间传递一个数组是这样的:

module foo
  use iso_c_binding
  private
  public :: bar
  interface
     subroutine bar(a,n) bind(C)
       import
       real(kind=c_double), dimension(*), intent(inout) :: a
       integer(c_size_t), value, intent(in) :: n
     end subroutine bar
  end interface
end module foo

你的 C 函数将是

void bar(double *a, size_t n)

编辑:

从 Fortran 调用 C 函数的方法是

program main
  use iso_c_binding
  use foo
  real(c_double), dimension(10) :: a
  call bar(a,size(a,kind=c_size_t))
  print *,a
end program main

编辑 2:

如果你真的想每次都拷贝入/拷贝出,你可以这样做

  subroutine bar2(array)
    real(kind=c_double), intent(inout), dimension(:) :: array
    real(kind=c_double), dimension(size(array)) :: a
    a = array  ! Copy in
    call bar(a,size(a,kind=c_size_t))
    array = a  ! Copy out
  end subroutine bar2
end module foo

但我不明白为什么这是必要的。

编辑 3:

如果您担心 C 和 Fortran 数据类型不匹配,您可以编写一个通用包装器来解决这个问题。这就是它的样子:

module foo
  use iso_c_binding
  implicit none
  private
  public :: bar
  interface
     subroutine bar_double(a,n) bind(C)
       import
       real(kind=c_double), dimension(*), intent(inout) :: a
       integer(c_size_t), value, intent(in) :: n
     end subroutine bar_double
  end interface

  interface
     subroutine bar_float(a,n) bind(C)
       import
       real(kind=c_float), dimension(*), intent(inout) :: a
       integer(c_size_t), value, intent(in) :: n
     end subroutine bar_float
  end interface

  interface bar
     module procedure bar_aux_double, bar_aux_float
  end interface bar
contains
  subroutine bar_aux_double (a)
    real(kind=c_double), dimension(:), intent(inout) :: a
    call bar_double (a, size(a,kind=c_size_t))
  end subroutine bar_aux_double

  subroutine bar_aux_float (a)
    real(kind=c_float), dimension(:), intent(inout) :: a
    call bar_float (a, size(a,kind=c_size_t))
  end subroutine bar_aux_float
end module foo

你的主程序看起来像

program main
  use foo
  integer, parameter :: dp = selected_real_kind(15)
  integer, parameter :: sp = selected_real_kind(6)
  real(dp), dimension(10) :: a_dp
  real(sp), dimension(10) :: a_sp
  call bar(a_dp)
  call bar(a_sp)
  print *,a_dp,a_sp
end program main

您根本没有提及 iso_c_binding。如果没有dp或sp的包装函数,编译会因为缺少通用过程而失败。

如果您使用模块,在 Fortran 中混合 dpc_double 时不要太担心。在极不可能的情况下,selected_real_kind(15, 307) /= c_double 编译器会在检查过程接口时报错。否则,它会发现类型数字是一致的,并且它不关心您如何调用类型常量(声明可互操作过程时除外)。