Question

I am calling a C function from a Fortran 90 program (I have to use Fortran 90). This C function takes a couple arguments and returns a float pointer. I cannot seem to print the returned data correctly in the Fortran code. 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. 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? (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...

Thanks!

Fortran Program

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

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
Was it helpful?

Solution 3

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. 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.

Cray pointers can be used in association with returned C pointers. In order to declare a Cray pointer in Fortran to a float array for example:

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

and then you can call the C function like so:

    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:

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

OTHER TIPS

This statement "I cannot use the ISO C bindings as that is only for Fortran 2003 and up." is odd; any current compiler that supports F90 also supports most or all of 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). 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:

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.

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:

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

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. 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.

Note that C interoperability from F2003 is an extension to F90...

(Cray pointers are too, but if you have to use an extension I know which one I'd pick.)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top