Lahey/GNU Fortran

Next: , Previous: Interoperable Subroutines and Functions, Up: Interoperability with C


7.1.5 Working with Pointers

C pointers are represented in Fortran via the special opaque derived type type(c_ptr) (with private components). Thus one needs to use intrinsic conversion procedures to convert from or to C pointers. For example,

       use iso_c_binding
       type(c_ptr) :: cptr1, cptr2
       integer, target :: array(7), scalar
       integer, pointer :: pa(:), ps
       cptr1 = c_loc(array(1)) ! The programmer needs to ensure that the
                               ! array is contiguous if required by the C
                               ! procedure
       cptr2 = c_loc(scalar)
       call c_f_pointer(cptr2, ps)
       call c_f_pointer(cptr2, pa, shape=[7])

When converting C to Fortran arrays, the one-dimensional SHAPE argument has to be passed.

If a pointer is a dummy-argument of an interoperable procedure, it usually has to be declared using the VALUE attribute. void* matches TYPE(C_PTR), VALUE, while TYPE(C_PTR) alone matches void**.

Procedure pointers are handled analogously to pointers; the C type is TYPE(C_FUNPTR) and the intrinsic conversion procedures are C_F_PROCPOINTER and C_FUNLOC.

Let us consider two examples of actually passing a procedure pointer from C to Fortran and vice versa. Note that these examples are also very similar to passing ordinary pointers between both languages. First, consider this code in C:

     /* Procedure implemented in Fortran.  */
     void get_values (void (*)(double));
     
     /* Call-back routine we want called from Fortran.  */
     void
     print_it (double x)
     {
       printf ("Number is %f.\n", x);
     }
     
     /* Call Fortran routine and pass call-back to it.  */
     void
     foobar ()
     {
       get_values (&print_it);
     }

A matching implementation for get_values in Fortran, that correctly receives the procedure pointer from C and is able to call it, is given in the following MODULE:

     MODULE m
       IMPLICIT NONE
     
       ! Define interface of call-back routine.
       ABSTRACT INTERFACE
         SUBROUTINE callback (x)
           USE, INTRINSIC :: ISO_C_BINDING
           REAL(KIND=C_DOUBLE), INTENT(IN), VALUE :: x
         END SUBROUTINE callback
       END INTERFACE
     
     CONTAINS
     
       ! Define C-bound procedure.
       SUBROUTINE get_values (cproc) BIND(C)
         USE, INTRINSIC :: ISO_C_BINDING
         TYPE(C_FUNPTR), INTENT(IN), VALUE :: cproc
     
         PROCEDURE(callback), POINTER :: proc
     
         ! Convert C to Fortran procedure pointer.
         CALL C_F_PROCPOINTER (cproc, proc)
     
         ! Call it.
         CALL proc (1.0_C_DOUBLE)
         CALL proc (-42.0_C_DOUBLE)
         CALL proc (18.12_C_DOUBLE)
       END SUBROUTINE get_values
     
     END MODULE m

Next, we want to call a C routine that expects a procedure pointer argument and pass it a Fortran procedure (which clearly must be interoperable!). Again, the C function may be:

     int
     call_it (int (*func)(int), int arg)
     {
       return func (arg);
     }

It can be used as in the following Fortran code:

     MODULE m
       USE, INTRINSIC :: ISO_C_BINDING
       IMPLICIT NONE
     
       ! Define interface of C function.
       INTERFACE
         INTEGER(KIND=C_INT) FUNCTION call_it (func, arg) BIND(C)
           USE, INTRINSIC :: ISO_C_BINDING
           TYPE(C_FUNPTR), INTENT(IN), VALUE :: func
           INTEGER(KIND=C_INT), INTENT(IN), VALUE :: arg
         END FUNCTION call_it
       END INTERFACE
     
     CONTAINS
     
       ! Define procedure passed to C function.
       ! It must be interoperable!
       INTEGER(KIND=C_INT) FUNCTION double_it (arg) BIND(C)
         INTEGER(KIND=C_INT), INTENT(IN), VALUE :: arg
         double_it = arg + arg
       END FUNCTION double_it
     
       ! Call C function.
       SUBROUTINE foobar ()
         TYPE(C_FUNPTR) :: cproc
         INTEGER(KIND=C_INT) :: i
     
         ! Get C procedure pointer.
         cproc = C_FUNLOC (double_it)
     
         ! Use it.
         DO i = 1_C_INT, 10_C_INT
           PRINT *, call_it (cproc, i)
         END DO
       END SUBROUTINE foobar
     
     END MODULE m