Iso_c_binding: callbacks, gfortran and flang

I maintain a Fortran library with a C interface, so that it can be called from Python, etc.
The library compiles with gfortran, but not with flang.

The code below is a minimal example of the approach I use to build the C-API. The tricky part is the handling of the callbacks.
gfortran apparently considers that the C-callback interface matches the Fortran-callback and does the “right” thing. flang, on the other hand, is strict and deems the interfaces incompatible because the Fortran interface does not have the bind(c) attribute.

I can “solve” the problem by adding bind(c) to func_t. However, then flang forces me to add bind(c) to the pure fortran callbacks, which is not very fortranic.

How can I address this issue in a proper manner without constraining the Fortran usage to the specific requirements of the C-API?

module lib
  use iso_c_binding, only: c_int, c_float, c_funptr, c_f_procpointer, c_funloc
  implicit none

  abstract interface
    subroutine func_t(n, x, res)
    ! Interface for Fortran callback
      integer, intent(in) :: n
      real, intent(in) :: x(n)
      real, intent(out) :: res(n)
    end subroutine func_t
    
    subroutine func_c_t(n, x, res) bind(c)
    ! Interface for C callback
    ! Identical to func_t but with C types and bind(c)
      import :: c_int, c_float
      integer(c_int), intent(in) :: n
      real(c_float), intent(in) :: x(n)
      real(c_float), intent(out) :: res(n)
    end subroutine func_c_t
  end interface
  
contains

  subroutine norm_func(f, n, x, fnorm)
  ! Main Fortran algorithm using Fortran callback
  ! Here just a trivial example computing the 2-norm of f(x)
    procedure(func_t) :: f
    integer, intent(in) :: n
    real, intent(in) :: x(n)
    real, intent(out) :: fnorm
    real :: res(n)

    call f(n, x, res)
    fnorm = norm2(res)

  end subroutine norm_func

  subroutine norm_func_c(f, n, x, fnorm) bind(c)
  ! C-wapper to main algorithm using a C callback
    procedure(func_c_t) :: f
    integer(c_int), intent(in) :: n
    real(c_float), intent(in) :: x(n)
    real(c_float), intent(out) :: fnorm

    ! gfortran works fine with or without this explicit conversion
    ! flang complains regardless
    type(c_funptr) :: fp
    procedure(func_t), pointer :: fptr
    fp = c_funloc(f)
    call c_f_procpointer(fp, fptr)
    
    call norm_func(f, n, x, fnorm)

  end subroutine norm_func_c

end module lib

program test
  use lib
  implicit none

  integer, parameter :: n = 3
  real :: x(n), fnorm
  integer :: i

  x = 3.0
  call norm_func(myfunc, n, x, fnorm)
  print *, fnorm

contains

  subroutine myfunc(n, x, res)
    integer, intent(in) :: n
    real, intent(in) :: x(n)
    real, intent(out) :: res(n)
    integer :: i
    res = x ** 2
  end subroutine myfunc

end program test

I’ve solved my own question! :slight_smile:

One must receive the C-callback as generic c_funptr and then cast it to the desired Fortran procedure pointer using call c_f_procpointer.

module lib
  use iso_c_binding, only: c_int, c_float, c_funptr, c_f_procpointer
  implicit none

  abstract interface
    subroutine func_t(n, x, res)
    ! Interface for Fortran callback
      integer, intent(in) :: n
      real, intent(in) :: x(n)
      real, intent(out) :: res(n)
    end subroutine func_t
  end interface
  
contains

  subroutine norm_func(f, n, x, fnorm)
  ! Main Fortran algorithm using Fortran callback
  ! Here just a trivial example computing the 2-norm of f(x)
    procedure(func_t) :: f
    integer, intent(in) :: n
    real, intent(in) :: x(n)
    real, intent(out) :: fnorm
    real :: res(n)

    call f(n, x, res)
    fnorm = norm2(res)

  end subroutine norm_func

  subroutine norm_func_c(fp, n, x, fnorm) bind(c)
  ! C-wapper to main algorithm using a C callback
    type(c_funptr), value :: fp
    integer(c_int), intent(in) :: n
    real(c_float), intent(in) :: x(n)
    real(c_float), intent(out) :: fnorm

    procedure(func_t), pointer :: f
    
    call c_f_procpointer(fp, f)
    
    call norm_func(f, n, x, fnorm)

  end subroutine norm_func_c

end module lib

program test
  use lib
  implicit none

  integer, parameter :: n = 3
  real :: x(n), fnorm
  integer :: i

  x = 3.0
  call norm_func(myfunc, n, x, fnorm)
  print *, fnorm

contains

  subroutine myfunc(n, x, res)
    integer, intent(in) :: n
    real, intent(in) :: x(n)
    real, intent(out) :: res(n)
    integer :: i
    res = x ** 2
  end subroutine myfunc

end program test
1 Like