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)
精彩评论