简体   繁体   English

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

[英]Cannot get data from a returned C float pointer in Fortran 90

I am calling a C function from a Fortran 90 program ( I have to use Fortran 90 ). 我正在从Fortran 90程序调用C函数( 我必须使用Fortran 90 )。 This C function takes a couple arguments and returns a float pointer. 这个C函数接受几个参数,并返回一个float指针。 I cannot seem to print the returned data correctly in the Fortran code. 我似乎无法在Fortran代码中正确打印返回的数据。 It just displays a very larger number (which I assume is the address of the pointer.) 它只是显示一个更大的数字(我认为这是指针的地址。)

I have had success in passing REAL Fortran variables as arguments, having the C function set them (as Fortran passes by reference) and accessing the data then. 我已经成功地通过传递REAL Fortran变量作为参数,使用C函数对其进行了设置(因为Fortran通过引用传递),然后访问了数据。 However, I have to return the pointer as a return variable as this is the method a legacy function used (which I am re-implementing.) 但是,我必须将指针作为返回变量返回,因为这是旧版函数使用的方法(我正在重新实现)。

Is there a way in Fortran 90 to access the data from a non-character (real, int, etc.) pointer returned from a C function? Fortran 90中是否有一种方法可以从C函数返回的非字符(实数,整数等)指针访问数据? (Please note: I cannot use the ISO C bindings as that is only for Fortran 2003 and up.) I've put an idea of what I'm trying to do below... (请注意:我不能使用ISO C绑定,因为它仅用于Fortran 2003及更高版本。)下面我已经介绍了我想做的事情...

Thanks! 谢谢!

Fortran Program 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 function 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

Output 产量

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

This statement "I cannot use the ISO C bindings as that is only for Fortran 2003 and up." 此语句“我不能使用ISO C绑定,因为仅用于Fortran 2003及更高版本。” is odd; 很奇怪 any current compiler that supports F90 also supports most or all of F2003. 当前任何支持F90的编译器也支持大部分或全部F2003。

As Eric Urban points out, it's easiest just to do the array allocation within fortran of course (where you actually can use array slicing or broadcasting to do the filling easier). 正如Eric Urban所指出的,当然,最简单的做法就是在fortran中进行数组分配(实际上,您实际上可以使用数组切片或广播来简化填充)。 But assuming there is some C routine you need to call that takes this form, just use the ISO_C_BINDING module for portable interfacing between C and 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

Compiling and running gives 编译并运行给出

$ 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

If you try to do this in some way to get around 7+ year old compilers limitations, there's a bunch of brittle non-portable ways to do it, but it's really not worth the heartache. 如果您尝试通过某种方式来达到大约7年以上的编译器局限性,那么会有很多脆弱的,不可移植的方法来实现,但是这确实不值得让人心痛。

Update : If you can't touch (or even recompile) some of the older fortran, then you can at least make an F2003 wrapper for the C program and have the older fortran link to it: 更新 :如果您无法触摸(甚至无法重新编译)某些较早的fortran,那么您至少可以为C程序制作F2003包装器,并拥有较早的fortran链接:

foowrapper.f90: 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: foo.f90:

program test_real
    use wrapper

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

    print *,"return: ",array
end

Compiling and running: 编译并运行:

$ 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

Put implicit none in your code and you'll see why you are not getting the result you wanted. 在代码中放入隐式的无,您将明白为什么没有得到想要的结果。

Array valued functions in Fortran are typically implemented by passing a "hidden" parameter that is the address in memory where the function should write its result. Fortran中的数组值函数通常通过传递“隐藏”参数来实现,该参数是内存中函数应将其结果写入其中的地址。 This doesn't have much in common with your use case. 这与您的用例没有太多共同点。

There is no way to do this if you stick with Fortran 90. You will need to use some sort of extension. 如果坚持使用Fortran 90,则无法执行此操作。您将需要使用某种扩展。

Note that C interoperability from F2003 is an extension to F90... 请注意,F2003的C互操作性是F90的扩展。

(Cray pointers are too, but if you have to use an extension I know which one I'd pick.) (Cray指针也是,但如果您必须使用扩展名,我知道我会选择哪个扩展名。)

I didn't end up implementing the wrapper solution. 我最终没有实现包装解决方案。 What seems to work for me for the situation of not being able to use Fortran 2003 is Cray pointers. 对于无法使用Fortran 2003的情况,似乎对我有用的是Cray指针。 I'm not sure what the viability of these are in newer compilers, but for the F90 compiler I'm using (Intel ifort 9.1), it works great. 我不确定这些在新的编译器中的可行性是什么,但是对于我正在使用的F90编译器(Intel ifort 9.1),它的工作原理很好。

Cray pointers can be used in association with returned C pointers. Cray指针可与返回的C指针结合使用。 In order to declare a Cray pointer in Fortran to a float array for example: 为了在Fortran中将Cray指针声明为float数组,例如:

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

and then you can call the C function like so: 然后可以像这样调用C函数:

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

Here is a resource that explains how to use Cray pointers: 这是说明如何使用Cray指针的资源:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM