Fortran接口调用一个C函数,该函数返回一个指向数组的指针



经过大量搜索,我发现我认为最接近我的问题的答案是在Fortran接口的堆栈溢出(SO(上调用一个返回指针的C函数(发布于近10年前!(

我引用这个例子是因为使用这个例子可以使代码保持简单,并且仍然说明了我的问题。

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

我已经用注释强调了我试图做出的返回数组指针的更改,但我已经没有什么想法了。(我可以说,"哦,在过去糟糕的日子里,我可以把一个整数等价于iarray(1(,并超出数组的大小",但我不会。有编码保护是好事,但有时会让人沮丧。(

我使用的是Visual Studio 2017和"英特尔Fortran并行_Studio_xe_2019_update_5_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
// ps_test_pointersC.cpp : 'Subroutine' only.
extern "C" {
int bar[3] = { 2, 3, 4 };

int *foofunc() {
return bar;
}
}

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

如果我将"(:("添加到f_p的定义中,代码编译时不会出错,但当我运行它时,程序失败,并在"call c_f_pointer(c_p,f_p("行出现运行时错误:"forrtl:severe(408(:fort:(7(:尝试在指针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

// ps_test_pointersC.cpp : 'Subroutine' only.

extern "C" {

int bar[3] = { 2, 3, 4 };

void foofunc(int *rtn) {
rtn = bar;
}

}

但是C函数中创建的指针在返回时从未被分配给C_ p(因此f_p从未被定义(。

阅读这个问题,我希望我没有处于编译器实现的前沿,并且已经暴露了一个限制收紧但不能处理所有用例之间的问题!

有解决办法吗?

您的C函数正在返回一个指针标量;您希望将此目标与Fortran数组相关联。这意味着你有申报

type(c_ptr) :: c_p                  ! <- scalar address
integer(c_int), pointer :: f_p(:)   ! <- array to associate

在对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

如果你不喜欢幻数3,你需要找到其他方法来获得这个数字(就像在C世界中调用这个函数一样(。您可以将长度作为一个额外的参数,如royvib的子例程示例,作为一个链接相关的额外变量,通过单独的查询调用(如字符数组如何使用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
#include "ISO_Fortran_binding.h"
int bar[3] = { 2, 3, 4 };

void foosub(CFI_cdesc_t* f_p) {
CFI_index_t nbar[1] = {3};
CFI_CDESC_T(1) c_p;
CFI_establish((CFI_cdesc_t* )&c_p, bar, CFI_attribute_pointer, CFI_type_int,
nbar[0]*sizeof(int), 1, nbar);
CFI_setpointer(f_p, (CFI_cdesc_t *)&c_p, NULL);
}

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

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

关于子程序方法,我认为我们可能需要在C/C++侧将c_p声明为int**(而不是int*(,以通过参数关联(而不是函数返回值(获得bar的地址。所以。。。

main.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(:)
integer(c_int) :: nsize
interface
subroutine foosub( c_p, nsize ) bind(c)
import :: c_ptr, c_int
implicit none  
type(c_ptr)    :: c_p    !<-- sends the pointer to c_p
integer(c_int) :: nsize  !<-- sends the pointer to nsize
end subroutine
end interface
call foosub( c_p, nsize )
call c_f_pointer( c_p, f_p, [nsize] )
print *, "nsize  = ", nsize
print *, "f_p(:) = ", f_p(:)
end program

cpp:

extern "C" {
int bar[3] = { 2, 3, 4 };

void foosub( int** rtn, int* nsize ) {
*rtn = bar;
*nsize = sizeof(bar) / sizeof(int);
}
}

编译&运行:

$ g++-10 -c sub.cpp
$ gfortran-10 -c main.f90
$ g++-10 main.o sub.o -lgfortran
$ ./a.out
nsize  =            3
f_p(:) =            2           3           4

最新更新