Array pointers from F90 to C?

Array pointers from F90 to C?

Is it possible to pass array pointers from F90 to C?
DIGITAL Fortran 90 User Manual for DIGITAL UNIX Systems (as well as the corresponding COMPAQ manual) describes how to pass scalar pointer data from F90 to C, but I could not found a clear indication whether this is possible with array pointers or not.

5 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

Yes - and the same manual describes the layout of the array descriptor that is passed. You'll need to "decode" that in your C code.

Steve

Steve - Intel Developer Support

Perhaps easier is just to return LOC(YourArray) (and dimensions in separate arguments if necessary). C treats arrays as pointers anyway. Coping with F90 pointers is necessary only if you really need advanced capabilities -- F90 array descriptors are much more powerful than C-style pointers, since they can point to a offsetted, fragmented piece of memory (such as Array(2:30:3)). Deciphering that in C code isn't trivial. Note the difference in assumed-shape (:) and assumed-size (*) arrays:

 
!Simple way - with assumed-size arrays only the address is passed: 
INTERFACE 
   SUBROUTINE C_routine(Array, Size) 
   !DEC$ATTRIBUTES C:: C_routine 
   REAL:: Array(*) 
   INTEGER:: Size 
   END SUBROUTINE 
END INTERFACE 
CALL C_routine(Array, nItems) 
// C code: 
void C_routine(float* Array, int Length) 

With assumed-shape arrays, however, array descriptor must be passed:

 
INTERFACE 
   SUBROUTINE C_ROUTINE(Array) 
   !DEC$ATTRIBUTES C:: C_routine 
   REAL:: Array(:) 
... 
CALL C_routine(Array(2:70:7)) 
 
void C_routine(struct FORTRAN_DESCRIPTOR Array); 
!decipher FORTRAN_DESCRIPTOR here. Fortran compiler does it for you, 
but for C you'd have to do it yourself. 

Jugoslav

Jugoslav
www.xeffort.com

Thank you Steve and Jugoslav for your fast responses, but I'm not sure if I can use the solutions suggested. This is because, as I realize now, my question was not properly set.

Well, here's what I'm actually trying to do. I'm coding with F90, but I would like to call a void function written in C. I have managed to write (in C) a jacket that gets (lots of both scalar and array) arguments when called from F90, constructs required C-structures and then calls the actual C-function. Until this, everything works fine. But getting the results (once again, both scalars and arrays) to F90 is a problem because I don't know the size of the resulting arrays in advance.
I was silly enough to think that if I get an array pointer from F90 to C, I can simply point it to an array returned by C-function and thereafter use the contents in F90 through the array pointer. Oh, it isn't simple as that? :-)

And the question is: how should I get the array data from C to F90?
Can I use the array descriptors Steve suggested?
One solution that came to my mind is to call a F90 subprogram from the jacket function and pass the arrays as arguments. The F90 subprogram then sets the data to module variables. Is this as silly idea as the one I had earlier? :-)

Juuso

Huhhh... still don't have enough info. The main problem is, who allocates/frees the memory? There are several approaches:

1) The most common approach is that caller allocates memory ("buffer") and passes the address and size of buffer to callee (as in my previous post(1)). Callee returns an error if buffer is insufficient (possibly also returns required buffer size); it's up to caller to reallocate bigger chunk and retry. Many Win32 APIs use that trick (EnumPrinters, RegQueryValueEx etc.) This approach has the disadvantage that it complicates the caller; it has the advantage that it keeps things simple and adheres to the principle "free memory in the same subsystem that allocated it".

2) Another approach (the one you asked) is that callee allocates memory (new/malloc/GlobalAlloc/etc.) and returns info on its starting address and size. This approach has a disadvantage that in clean implementations there has to be another routine for freeing that memory. I'd subdivide the approach into two applicable for your situation:
a) Pass two integers by reference: lpAddress and nSize. Let lpAddress be a INTEGER ("Cray") pointer to an array:

REAL:: fArray(*); POINTER(lpAddress, fArray) 
INTERFACE 
   SUBROUTINE C_routine(lp, nSize) 
   !DEC$ATTRIBUTES C:: C_routine 
   !DEC$ATTRIBUTES REFERENCE:: lp, nSize 
... 
call C_routine(lpAddress, nSize) 
WRITE(*,*) (fArray(i), i=1,nSize) 
//C code-------------------------------- 
void C_routine (float* lpAddress, int& nSize) { 
//calculate nSize somehow 
lpAddress = malloc(nSize*sizeof(float)) 
//fill in lpAddress[1..nsize]

"Cray" pointers are basically the same as C pointers (though this is an extension to Fortran) so this approach is "C-like"

b) Use structure (2) as in my previous post. Declare fArray a POINTER in the caller. You'd have to find out descriptor format from docs, declare corresponding struct FORTRAN_DESCRIPTOR in C (IIRC the first member would be starting address, boundaries are also somewhere there). In C_routine, allocate array and fill in the structure accordingly.

Note that in both 2a) and 2b) you'd have to provide another function for freeing malloc()-ed arrays.

HTH

Jugoslav

Jugoslav
www.xeffort.com

Leave a Comment

Please sign in to add a comment. Not a member? Join today