6.1.5 Working with C Pointers

C pointers are represented in Fortran via the special opaque derived type type(c_ptr) (with private components). C pointers are distinct from Fortran objects with the POINTER attribute. Thus one needs to use intrinsic conversion procedures to convert from or to C pointers. For some applications, using an assumed type (TYPE(*)) can be an alternative to a C pointer, and you can also use library routines to access Fortran pointers from C. See Further Interoperability of Fortran with C.

Here is an example of using C pointers in Fortran:

  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