问题描述
经过大量搜索,我发现最接近我问题的答案是在Fortran interface to call a C function that return a pointer上的Stack Overflow(SO)上(发布于将近10年前!)
之所以这样引用,是因为使用该示例可使代码保持简单,并且仍然说明了我的问题。
我想返回一个已经在C ++中创建/分配的内存的数组,并且能够在Fortran中分析答案,因为这是此应用程序的大部分代码所在的地方。我的应用程序进入C ++生成整数数组答案,并通过C接口将其返回给Fortran程序。原始的SO示例使用单个双精度变量作为返回值。我将其更改为整数,因为这就是我将在应用程序中处理的内容。示例代码(已更改)有效。
我已在注释中突出显示了为返回数组指针而试图进行的更改,但是我已经没有足够的想法了。 (我可以说:“哦,在糟糕的过去,我可以将整数等同于iarray(1)并超出数组的大小。”但我不会。拥有编码保护是很好的,但有时令人沮丧。)
我正在使用Visual Studio 2017和Intel Fortran parallel_studio_xe_2019_update5_composer。
! 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的定义中添加'(:)',则代码将正确编译,但是当我运行它时,程序将由于运行时错误而失败:“ forrtl:严重(408):要塞: (7):在“调用c_f_pointer(c_p,f_p)”行尝试使用与目标不关联的指针F_P。
我尝试将c_p声明为数组(“ c_p(:)”),但在同一位置却遇到相同的错误。
我还尝试将c_p用作子例程的参数-仍然仅使用整数:
! ps_test_pointers.f90
program foo
use,&
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,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语言世界中调用此函数一样)。您可以将长度作为额外的参数,例如roygvib's subroutine,作为链接相关的额外变量,可以通过单独的查询调用(例如,字符数组如何使用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*
),以获得{{1}的地址。 }通过参数关联(而不是函数返回值)。所以像...
main.f90:
bar
sub.cpp:
program foo
use,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,[nsize] )
print *,"nsize = ",nsize
print *,"f_p(:) = ",f_p(:)
end program
编译并运行:
extern "C" {
int bar[3] = { 2,4 };
void foosub( int** rtn,int* nsize ) {
*rtn = bar;
*nsize = sizeof(bar) / sizeof(int);
}
}