ISO/IEC JTC1/SC22/WG5-N1442 Another look at dynamic arrays and interfacing with C John Reid Disclaimer: John would like to make it clear that he is writing this paper as an individual and not as Convener. I wrote N1441 with Aleksandar Donev because of our belief that the present proposal for interoperability with C (J3/01-007R2, section 15) has a serious deficiency. It is impossible for a Fortran procedure, while being called from C, to allocate an array and make that array available to the C code. Conversely, it is impossible for a C function, while being called from Fortran, to allocate an array and make that array available to the Fortran code. We have both found a need for this feature within our workplaces. John has colleagues who call Fortran from C extensively and wish to be able to use structures with allocatable components to hold the data pertinent to each particular problem. Aleksandar often interfaces with C graph and sparse-matrix libraries, which return results in the form of dynamic C arrays, that also need to be used in Fortran. I sent a copy to N1441 to the WG5 email list and obtained the response from Richard Maine that is attached as Annex 1. His view is that 1. it is already possible, by using the C_LOC intrinsic, for a Fortran procedure, while being called from C, to allocate an array and make that array available to the C code, and 2. it would be possible, by adding another intrinsic that constructs a Fortran pointer from a C pointer, for an array that is allocated in C to be available to Fortran code. A possible extra intrinsic was proposed in J3/00-168, which is attached as Annex 2. The way I read the draft standard is that 15.2.5 does not include allocatable arrays in the set of entities that are interoperable and C_LOC requires X to be interoperable. On the other hand, in C_LOC I find 'It shall not be an array pointer, an assumed shape array, or an array section', which does not exclude an allocatable array. I would like edits to be made that make it clear that data in an allocatable array can be accessed from C. I would also like consideration to be given to adding a means of accessing in Fortran an array allocated in C, perhaps by adding an extra intrinsic on the lines of that proposed in J3/00-168. I do not want the proposal in N1441 to be considered. .................................................................... ANNEX 1. Message from Richard Maine. I won't be at the London meeting, and the J3 meeting before it has already happened, so email is my only avenue for commenting on this. (That in itself concerns me - to have what looks like a major new proposal with this little chance for review before it possibly becomes a new requirement.) This seems like an awfully large new requirement/proposal for this late in the process. We are now supposed to be in the integration phase, which is all to short for a good job as is. I would expect this to substantially impact integration. > It is impossible for a Fortran procedure, while > being called from C, to allocate an array and make that array available > to the C code. I do not believe that to be the case. It appears to me that the Fortran subroutine could use a saved, target, allocatable array, in conjunction with the C_LOC intrinsic to do such a thing. I agree the use of allocatable would place some pretty significant restrictions on the usability of this. I would think that allowing C_LOC to take pointer arguments would be a far simpler way of extending this. Even if we add some new definitions to restrict the class of pointers it could reference to those that were "contiguous", I'd think it a lot less complication to define that than to introduce a whole new class of pointer, with a new syntax and lots of new interactions to worry about. > Conversely, it is impossible for a C function, while > being called from Fortran, to allocate an array and make that array > available to the Fortran code. Agree. And a hugely simpler solution to that was proposed and rejected. To my knowledge, the only reason for the rejection was that it did allow Fortran pointers to point to C allocated things. I don't think the rejection was based on the specific mechanism so much as the end result. If we want to allow this, I propose that we resurrect that idea, which was very simple and really had no other complications that I can think of. (Ok, I'm biased; I liked the idea before, and I still do.) That idea was basically to have an intrinsic that generated a Fortran pointer from an address and a mold. See paper J3/00-168. One could argue spelling trivia, but the idea seems pretty simple and workable to me. > It also has the side benefit of providing a dynamic array within > Fortran that has far less storage overheads than allocatable or > pointer arrays. I don't believe that side effect is, in itself, of major enough impact to justify such a late proposal. The C/Fortran interop aspects, which I do agree are important, have, in my opinion, simpler solutions, as described above. I would think it more appropriate to pursue those solutions than this new syntax. In particular, as long as we allow some way to dereference C pointers, it is going to be hard (in my opinion) to explain why we don't have something like the proposal of paper j3/00-168. So let's see how much of the problem that solves by itself. .................................................................... ANNEX 2 J3/00-168 Date: 2000/05/08 To: J3 From: William Mitchell Subject: Fortran pointer from C pointer References: 00-007, 00-121, 00-149, 00-150 Several deficiencies in C interoperability were presented in 00-121. Many of these were resolved in 00-149 and 00-150, and some were determined to be impossible to address, however three important issues were determined to be "too hard" to address at this time. These are: - Provide a means by which a character string of unknown length can be returned from a C function, both as an argument and as a function result. - Provide a means by which an array allocated in a C function can be returned to a calling Fortran procedure. - Provide a means by which a C pointer to a function can be returned to Fortran and assigned to a procedure pointer. All of these issues can be easily solved by providing a means of constructing a Fortran pointer from a C pointer. This paper proposes that a function be added to the ISO_C_BINDING module to provide this functionality. The function takes a C pointer, a mold to determine the result type, and, if the result is an array, a shape and returns a Fortran pointer associated with the same target as the C pointer. The author is not married to the name CPTR_TO_FPTR. Edits to 00-007: [387:46] Add: The CPTR_TO_FPTR function provides a means of defining a Fortran pointer that is associated with the same target as a C pointer. CPTR_TO_FPTR(CPTR,MOLD [,SHAPE]) Description. Constructs a pointer associated with the same target as CPTR and with the characteristics of MOLD. Class. Transformational function. Arguments. CPTR shall be a scalar of type C_PTR MOLD shall be a pointer and may be of any type or may be a procedure pointer. Its pointer association status may be undefined, disassociated or associated. If its status is associated, the target need not be defined with a value. SHAPE (optional) shall be of type integer, rank one, and constant size. It shall be present if and only if MOLD is an array pointer. Its size shall be equal to the rank of MOLD. It shall not have an element whose value is negative. Result Characteristics. The result is of the same type and type parameters as MOLD. Case (i): If MOLD is a scalar pointer, the result is a scalar pointer. Case (ii): If MOLD is an array pointer, the result is an array pointer of shape SHAPE. Case (iii): If MOLD is a procedure pointer, the result is a procedure pointer with an implicit interface. Result Value. The result is associated with the same target as CPTR. NOTE 16.x The following example illustrates how to access a C character string that was allocated and set by a procedure with the prototype void getstring(char *string, int *nchar) by using either a pointer to scalar character string of appropriate length or an array of characters of length one. USE ISO_C_BINDING INTERFACE BIND(C,NAME="getstring") SUBROUTINE GETSTRING(STRING,NCHAR) USE ISO_C_BINDING TYPE(C_PTR) :: STRING ! NOTE it is not VALUE INTEGER(C_INT), INTENT(OUT) :: NCHAR END SUBROUTINE GETSTRING END INTERFACE TYPE(C_PTR) :: C_STRING INTEGER(C_INT) :: NCHAR CHARACTER(LEN=:,KIND=C_CHAR), POINTER :: SCALAR_STRING CHARACTER(LEN=1,KIND=C_CHAR), POINTER, DIMENSION(:) :: ARRAY_STRING CALL GETSTRING(C_STRING,NCHAR) ALLOCATE(SCALAR_STRING(NCHAR)) SCALAR_STRING => CPTR_TO_FPTR(C_STRING,SCALAR_STRING) ALLOCATE(ARRAY_STRING(NCHAR)) ARRAY_STRING => CPTR_TO_FPTR(C_STRING,ARRAY_STRING,(/NCHAR/)) [388:5-6] Delete Note 16.7