Fortran 接口调用返回指向数组的指针的 C 函数

Geo*_*ffH 3 arrays fortran pointers interface fortran-iso-c-binding

经过多次搜索,我发现我认为最接近我的问题的答案是在Fortran 接口的Stack Overflow (SO) 上调用返回指针的 C 函数,(近 10 年前发布!)

我引用这一点是因为使用该示例可以使代码保持简单并仍能说明我的问题。

我想返回一个已在 C++ 中创建/分配的内存的数组,并能够在 Fortran 中分析答案,因为这是该应用程序的大部分代码所在。我的应用程序进入 C++ 以生成整数数组答案,并通过 C 接口将其返回给 Fortran 程序。原始 SO 示例使用单个双精度变量作为返回值。我已将其更改为整数,因为这是我将在我的应用程序中处理的内容。示例代码(已更改)有效。

我在评论中强调了我试图为返回数组指针所做的更改,但我已经没有想法了。(我可以说,“哦,对于那些糟糕的过去,我可以将一个整数等同于一个 iarray(1) 并超出数组的大小”,但我不会。有编码保护是很好的,但有时它变得令人沮丧。)

我正在使用 Visual Studio 2017 和 Intel Fortran parallel_studio_xe_2019_update5_composer。

我对原始 SO 代码的修改示例:

! ps_test_pointers.f90

program foo
  use, intrinsic :: iso_c_binding, only : c_ptr,        &
                                          c_f_pointer,  &
                                          c_int
  implicit none
  type(c_ptr) :: c_p!(:) ! <-------
  integer(c_int), pointer :: f_p!(:) ! <-------

  interface
    function foofunc() bind(c)
      import :: c_ptr
      implicit none  
      type(c_ptr) :: foofunc!(:) ! <-------
    end function foofunc
  end interface

  c_p = foofunc()
  call c_f_pointer(c_p, f_p)
  print *, f_p

end program foo
Run Code Online (Sandbox Code Playgroud)
! ps_test_pointers.f90

program foo
  use, intrinsic :: iso_c_binding, only : c_ptr,        &
                                          c_f_pointer,  &
                                          c_int
  implicit none
  type(c_ptr) :: c_p!(:) ! <-------
  integer(c_int), pointer :: f_p!(:) ! <-------

  interface
    function foofunc() bind(c)
      import :: c_ptr
      implicit none  
      type(c_ptr) :: foofunc!(:) ! <-------
    end function foofunc
  end interface

  c_p = foofunc()
  call c_f_pointer(c_p, f_p)
  print *, f_p

end program foo
Run Code Online (Sandbox Code Playgroud)

正如我上面所说的,代码有效,因为它打印出数组的第一个元素 ('2')。

如果我将 '(:)' 添加到 f_p 的定义中,则代码编译没有错误,但是当我运行它时,程序失败并显示运行时错误:“forrtl:severe (408): fort: (7) : 在“call c_f_pointer(c_p, f_p)”行中,当指针与目标不相关时,尝试使用指针 F_P。

我曾尝试将 c_p 声明为一个数组(“c_p(:)”),但我在同一个地方遇到了同样的错误。

我也试过调用 c_p 作为子程序的参数——仍然只使用整数:

    ! ps_test_pointers.f90
    
    program foo
      use, intrinsic :: iso_c_binding, only : c_ptr,        &
                                              c_f_pointer,  &
                                              c_int
      implicit none
      type(c_ptr) :: c_p!(:) ! <-------
      integer(c_int), pointer :: f_p!(:) ! <-------
    
      interface
        subroutine foofunc(c_p) bind(c)
          import :: c_ptr
          implicit none  
          type(c_ptr) :: c_p!(:) ! <-------
        end subroutine foofunc
      end interface
    
      call foofunc(c_p)
      call c_f_pointer(c_p, f_p)
      print *, f_p
    
    end program foo
    
Run Code Online (Sandbox Code Playgroud)
// ps_test_pointersC.cpp : 'Subroutine' only.

extern "C" {

    int bar[3] = { 2, 3, 4 };
    
    int *foofunc() {
        return bar;
    }

}
Run Code Online (Sandbox Code Playgroud)

但是在 C 函数中创建的指针永远不会在返回时分配给 c_p(因此从未定义 f_p)。

阅读这个问题,我希望我没有处于编译器实现的最前沿,并且暴露了限制收紧但无法应对所有用例之间的问题!

这个问题有方法解决吗?

fra*_*lus 5

你的 C 函数返回一个指针标量;您想将此目标与 Fortran 数组相关联。这意味着你有声明

type(c_ptr) :: c_p                  ! <- scalar address
integer(c_int), pointer :: f_p(:)   ! <- array to associate
Run Code Online (Sandbox Code Playgroud)

在调用中c_f_pointer使用另一个参数指定 Fortran 指针数组的形状。但是,在这种情况下,Fortran 端无法知道 C 函数返回的数组有多大。

考虑:

use, intrinsic :: iso_c_binding
implicit none

type(c_ptr) :: c_p
integer(c_int), pointer :: f_p(:)

interface
    function foofunc() bind(c)
      import :: c_ptr
      implicit none  
      type(c_ptr) :: foofunc
    end function foofunc
end interface

c_p = foofunc()
call c_f_pointer(c_p, f_p, [3])
print *, f_p

end
Run Code Online (Sandbox Code Playgroud)

如果您不喜欢幻数3,则需要找到其他方法来获取该数字(就像在 C 世界中调用此函数一样)。您可以将长度作为额外参数,就像roygvib 的子例程示例一样,作为链接关联的额外变量,通过单独的查询调用(例如如何使用字符数组strnlen)等。

或者,如果您想非常花哨并且在语言界面方面具有灵活性,则可以在 C 子例程中使用“改进的互操作性”功能来进行 Fortran 内存管理:

program foo
  use, intrinsic :: iso_c_binding, only : c_int

  implicit none
  integer(c_int), pointer :: f_p(:)

  interface
     subroutine foosub(f_p) bind(c)
       import c_int
       implicit none  
       integer(c_int), pointer, intent(out) :: f_p(:)
     end subroutine foosub
  end interface
    
  call foosub(f_p)
  print *, f_p
    
end program foo
Run Code Online (Sandbox Code Playgroud)
type(c_ptr) :: c_p                  ! <- scalar address
integer(c_int), pointer :: f_p(:)   ! <- array to associate
Run Code Online (Sandbox Code Playgroud)

如果您愿意,也可以使用可分配变量而不是指针变量。

这种方法不适用于 Fortran 函数,因为可互操作的函数不能有数组、指针或可分配的结果。