开发者

Fortran: Array of pointer arrays?

I'm working with some Fortran code (which I'd never used until this project...) and have come up against a problem. I need to share some memory space w开发者_StackOverflow社区ith another program. To get Fortran to recognise each memory chunk I use the following code:

       do 10 i = 0, 5 
       CALL C_F_POINTER(TRANSFER(memory_location +
       : VarNamesLoc_(i), 
       : memory_location_cptr) , VarNames_(i), [3]) 
       exit 
    10 continue

Where:

VarLoc(i) is an integer representing the memory location

VarNames(i) ?Array of pointer arrays?

The problem I'm having is creating the VarNames array of pointer arrays. I've found some sample code from my googling but I find Fortran quite difficult to understand!! Can anyone show me how to set up an array of pointer arrays? Or point out an alternative if I'm approaching the problem incorrectly?

For reference the Fortran code is written in free form and uses an intel compiler

Thanks for any help!


Ok, so I'm going to assume the colons have something to do with continuation lines and ignore them, and that you're trying to do this:

do i = 0, 5 
    CALL C_F_POINTER(&
          TRANSFER(memory_location + VarNamesLoc_(i), memory_location_cptr), &
          VarNames_(i), [3] ) 
enddo 

What this is doing is: (the TRANSFER) taking an integer representing existing C pointer (I assume) memory_location, add an offset to that (VarNamesLoc_(i)), and turn it into a type of c_ptr. And then (the C_F_POINTER) converting that to a Fortran pointer of shape [3].

I don't think doing the C-pointers arithmetic on the Fortran side is a great idea, but.

So you want VarNames_ to be an array of 5 pointers to arrays of 3.. somethings, you haven't said. Let's say integers.

Let's take it from the simple case: let's just say we had a 1 dimensional array of ints in C, and wanted a pointer to them in Fortran. If our C routine was this (croutine.c):

#include <stdio.h>
#include <stdlib.h>

void makearray(int **data, int n) {
    *data = (int *)malloc(n * sizeof(int));
    for (int i=0; i<n; i++) 
        (*data)[i] = i;
    return;
}

void freearray(int **data, int n) {
    free(*data);
    return;
}

our Fortran driver might look like this (driver.f90):

PROGRAM interoptesting
    USE, intrinsic :: iso_c_binding
    USE, intrinsic :: iso_fortran_env
    IMPLICIT NONE

    INTERFACE
        !! C prototype: void makearray(int **data, int n)
        SUBROUTINE makearray(data, n) BIND(C)
            USE, intrinsic :: iso_c_binding
            type(c_ptr)    :: data
            integer(kind=c_int), value :: n
        END SUBROUTINE makearray
        !! C prototype: void freearray(int **data, int n)
        SUBROUTINE freearray(data, n) BIND(C)
            USE, intrinsic :: iso_c_binding
            type(c_ptr)    :: data
            integer(kind=c_int), value :: n
        END SUBROUTINE freearray
    END INTERFACE 

    type(c_ptr) :: cdata
    integer, pointer, dimension(:) :: fdata
    integer                        :: n = 5

    call makearray(cdata, n);
    call c_f_pointer(cdata, fdata, [n])
    print *, 'fdata = ', fdata
    call freearray(cdata, n)

END program

and a Makefile like this:

FC=gfortran
CC=gcc
CFLAGS=-std=c99 -g
FFLAGS=-g

main: driver.o croutine.o 
        $(FC) -o $@ $^

driver.o: driver.f90
        $(FC) $(FFLAGS) -c $<

clean:
        rm -rf main driver.o croutine.o 

and building it and running it we get the expected answer:

$ ./main 
 fdata =            0           1           2           3           4

Note that in C we allocated an array of 5 ints; the Fortran program is mostly defining the interface to the C routines so we can call them from Fortran, then calling them. The c_f_pointer does the translation between the c pointer (cdata) and the Fortran pointer (fdata).

If we wanted to do some C-pointer arithmetic in Fortran, we could do this:

type(c_ptr) :: cdata, newdata
integer(kind=int64)            :: tmpint
integer, pointer, dimension(:) :: fdata
integer                        :: n = 5
integer(kind=c_int) :: cint

call makearray(cdata, n);

! copy pointer to an int
tmpint = TRANSFER(cdata, tmpint)
! add two integer sizes:
tmpint = tmpint + 2*c_sizeof(cint)
! copy back into a pointer
newdata= TRANSFER(tmpint, newdata)

call c_f_pointer(newdata, fdata, [n-2])
print *, 'fdata = ', fdata
call freearray(cdata, n)

But I really wouldn't recommend this; better to do the pointer manipulations in Fortran:

type(c_ptr) :: cdata
integer, pointer, dimension(:) :: fdata, newfdata
integer                        :: n = 5

call makearray(cdata, n);
call c_f_pointer(cdata, fdata, [n])

newfdata => fdata(3:n)
print *, 'newfdata = ', newfdata
call freearray(cdata, n)

Cleaner, less likely to cause wierd errors, and smaller, too!

Ok, so finally, let's do an array of pointers. This, admittedly, is harder than it should be in Fortran because Fortran doesn't easily let you define arrays of pointers; you have to create a defined type (the Fortran equivalent of a struct in C). But it's easy enough. Let's do things the way I recommend, doing the pointer math on the Fortran side:

type(c_ptr) :: cdata
integer, pointer, dimension(:) :: fdata
integer                        :: n = 10
integer, parameter             :: nptrs = 5
integer :: i, intsperptr, istart, iend

! our new "ptrelement" type which we can define arrays of
type ptrelement
     integer, pointer, dimension(:) :: p
end type ptrelement
type(ptrelement) :: ptrs(nptrs)

call makearray(cdata, n);
call c_f_pointer(cdata, fdata, [n])

intsperptr = n/nptrs
do i=1,nptrs
    istart = (i-1)*intsperptr+1
    iend   = istart + intsperptr-1
    ptrs(i)%p => fdata(istart:iend)
enddo

do i=1,nptrs
    print '(A,I2,A,99(I5,X))', 'ptrs(',i,')%p = ', ptrs(i)%p
enddo

call freearray(cdata, n)

Here we've created a type ptrelelment which is a 1-d pointer array, then created an array of those. That gives us our array of pointers, which we set by taking slices of fdata, which is still the pointer to the whole data.

running gives us

$ ./main
ptrs( 1)%p =     0     1
ptrs( 2)%p =     2     3
ptrs( 3)%p =     4     5
ptrs( 4)%p =     6     7
ptrs( 5)%p =     8     9

Or, as I do not suggest, doing the C-style pointer math in Fortran:

type(c_ptr) :: cdata
integer                        :: n = 10
integer, parameter             :: nptrs = 5
integer :: i, intsperptr
integer(kind=c_int) :: cint
integer(kind=int64) :: cdata_as_int

! our new "ptrelement" type which we can define arrays of
type ptrelement
     integer, pointer, dimension(:) :: p
end type ptrelement
type(ptrelement) :: ptrs(nptrs)

call makearray(cdata, n);
cdata_as_int = TRANSFER(cdata, cdata_as_int)

intsperptr = n/nptrs
do i=1,nptrs
    call c_f_pointer( &
         TRANSFER(cdata_as_int + (i-1)*intsperptr*c_sizeof(cint), cdata),&
         ptrs(i)%p, [intsperptr] )
enddo

do i=1,nptrs
    print '(A,I2,A,99(I5,X))', 'ptrs(',i,')%p = ', ptrs(i)%p
enddo

call freearray(cdata, n)
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜