无法从 Fortran 90 中返回的 C 浮点指针获取数据

Posted

技术标签:

【中文标题】无法从 Fortran 90 中返回的 C 浮点指针获取数据【英文标题】:Cannot get data from a returned C float pointer in Fortran 90 【发布时间】:2013-03-12 19:40:01 【问题描述】:

我正在从 Fortran 90 程序调用 C 函数(我必须使用 Fortran 90)。这个 C 函数接受几个参数并返回一个浮点指针。我似乎无法在 Fortran 代码中正确打印返回的数据。它只是显示一个非常大的数字(我假设它是指针的地址。)

我已经成功地将 REAL Fortran 变量作为参数传递,让 C 函数设置它们(因为 Fortran 通过引用传递)并随后访问数据。但是,我必须将指针作为返回变量返回,因为这是旧函数使用的方法(我正在重新实现。)

在 Fortran 90 中有没有办法从 C 函数返回的非字符(实数、整数等)指针访问数据? (请注意:我不能使用 ISO C 绑定,因为这仅适用于 Fortran 2003 及更高版本。)我已经在下面说明了我想要做什么......

谢谢!

Fortran 程序

program test_real
    real, dimension(10) :: realpt
    integer nbr
    integer i
    real :: a=1.0
    real :: b=2.0
    real :: c=3.0

    nbr = 9
    realpt = getpointer(a, b, c)

    do 10 i = 1, nbr
        print *,"return: ",realpt(i)
10  continue

stop
End

C 函数

float* getpointer(float *a, float *b, float *c) 
    float *rfl = (float *) calloc(9,sizeof(float));
    int i=0;

    for(i=0;i<3;i++) 
        rfl[i] = *a;
    
    for(i=3;i<6;i++) 
        rfl[i] = *b;
    
    for(i=6;i<9;i++) 
        rfl[i] = *c;
    
    return(rfl);
   // End of getpointer function

输出

return:   3.1661344E+07
return:   3.1661344E+07
return:   3.1661344E+07
return:   3.1661344E+07
return:   3.1661344E+07
return:   3.1661344E+07
return:   3.1661344E+07
return:   3.1661344E+07
return:   3.1661344E+07

【问题讨论】:

不需要使用fortran 90动态数组语法吗? 只是在黑暗中的狂野射击,会不会是调用约定不匹配? 【参考方案1】:

此声明“我不能使用 ISO C 绑定,因为它仅适用于 Fortran 2003 及更高版本。”很奇怪;当前任何支持 F90 的编译器也支持大部分或全部 F2003。

正如 Eric Urban 所指出的,在 fortran 中进行数组分配当然是最简单的(实际上您可以使用数组切片或广播来更轻松地进行填充)。但是假设您需要调用一些采用这种形式的 C 例程,只需使用 ISO_C_BINDING 模块在 C 和 fortran 之间进行可移植接口:

program test_real
    use, intrinsic :: iso_c_binding
    real(kind=c_float), pointer :: realpt(:)
    type(c_ptr) :: ptr
    integer :: nbr
    integer :: i
    real :: a=1.0
    real :: b=2.0
    real :: c=3.0

    interface
       function getpointer(a, b, c) result(ptr) bind(C,name="getpointer")
           use, intrinsic :: iso_c_binding
           implicit none
           type(c_ptr) :: ptr
           real(kind=c_float) :: a, b, c
        end function getpointer
      end interface

    nbr = 9
    ptr = getpointer(a, b, c)
    call c_f_pointer(ptr, realpt, [nbr])

    print *,"return: ",realpt
end

编译运行给出

$ gcc -c fooc.c
$ gfortran -c foo.f90
$ gfortran -o foo foo.o fooc.o
$ ./foo
 return:    1.0000000       1.0000000       1.0000000       2.0000000       2.0000000       2.0000000       3.0000000       3.0000000       3.0000000

如果您尝试以某种方式执行此操作来绕过 7 年以上的编译器限制,那么有很多脆弱的不可移植的方法可以做到这一点,但这真的不值得心痛。

更新:如果您不能接触(甚至重新编译)一些较旧的 fortran,那么您至少可以为 C 程序制作一个 F2003 包装器,并将较旧的 fortran 链接到它:

foowrapper.f90:

module wrapper

contains

subroutine wrapgetpointer(outarr)
    use, intrinsic :: iso_c_binding
    implicit none

    real, intent(out), dimension(:) :: outarr
    real(kind=c_float), pointer :: realpt(:)
    type(c_ptr) :: ptr
    integer :: nbr = 9
    real(kind=c_float) :: a, b, c

    interface
       function getpointer(a, b, c) result(ptr) bind(C,name="getpointer")
           use, intrinsic :: iso_c_binding
           implicit none
           type(c_ptr) :: ptr
           real(kind=c_float) :: a, b, c
        end function getpointer
      end interface

    a = 1.
    b = 2.
    c = 3.
    ptr = getpointer(a, b, c)
    call c_f_pointer(ptr, realpt, [nbr])

    outarr(1:nbr) = realpt
end subroutine wrapgetpointer

end module wrapper

foo.f90:

program test_real
    use wrapper

    real, dimension(9) :: array
    call wrapgetpointer(array)

    print *,"return: ",array
end

编译运行:

$ gfortran -c foowrapper.f90
$ gfortran -c foo.f90
$ gcc -c fooc.c
$ gfortran -o foo foo.o foowrapper.o fooc.o
$ ./foo
 return:    1.0000000       1.0000000       1.0000000       2.0000000       2.0000000       2.0000000       3.0000000       3.0000000       3.0000000

【讨论】:

是的,我知道这是一个奇怪的要求。对我来说更重要的是,目前,我不能保证所有将使用 C 函数的 Fortran 程序都是使用可以使用 ISO C 绑定的编译器编译的。我不知道是否有办法以 Fortran 可以读取的方式返回浮点指针。 在这种情况下,我的建议是编写(并编译)一个调用 C 的 F2003 包装例程,然后让旧的 fortran 调用该例程。 答案已相应更新。虽然如果你真的无法控制旧代码的编译,任何类型的互操作性都可能是不可能的。【参考方案2】:

在你的代码中添加隐式 none,你就会明白为什么你没有得到你想要的结果。

Fortran 中的数组值函数通常通过传递一个“隐藏”参数来实现,该参数是函数应该写入其结果的内存地址。这与您的用例没有太多共同之处。

如果您坚持使用 Fortran 90,则无法做到这一点。您需要使用某种扩展。

请注意,从 F2003 开始​​的 C 互操作性是对 F90 的扩展...

(Cray 指针也是,但如果你必须使用扩展,我知道我会选择哪一个。)

【讨论】:

【参考方案3】:

我最终没有实施包装解决方案。对于无法使用 Fortran 2003 的情况,似乎对我有用的是 Cray 指针。我不确定这些在较新的编译器中的可行性,但对于我正在使用的 F90 编译器(英特尔 ifort 9.1),它工作得很好。

Cray 指针可以与返回的 C 指针结合使用。为了在 Fortran 中将 Cray 指针声明为浮点数组,例如:

real, dimension(10) :: realpt
pointer (ptemp,realpt) 
nbr = 9

然后你可以像这样调用C函数:

    ptemp = getpointer(a, b, c)    
    do 10 i = 1, nbr        
        print *,"return: ",realpt(i)
10  continue

这是一个解释如何使用 Cray 指针的资源:

http://gcc.gnu.org/onlinedocs/gfortran/Cray-pointers.html

【讨论】:

以上是关于无法从 Fortran 90 中返回的 C 浮点指针获取数据的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Inquire pos 在 Fortran 90 中返回 0

在 Fortran90 中从文本文件中跳过一行

Fortran数组范围检查的运行时检查未触发

将 mpi 消息从 c++ 代码发送到 fortran 90 代码

访问Fortran中返回数组的元素

如何在 Fortran 90 中跳过带有“#”符号的行? [复制]