Fortran-C interoperability and float arrays

c, fortran, fortran-iso-c-binding

Solution

First, I assume you define dp as a parameter in a module somewhere. You can simply use

integer, parameter :: dp = c_double

in that module (and have `if (dp /= c_double) stop "Bletchful sytem"` somewhere.

Passing an array between C and Fortran works like this:

module foo
  use iso_c_binding
  private
  public :: bar
  interface
     subroutine bar(a,n) bind(C)
       import
       real(kind=c_double), dimension(*), intent(inout) :: a
       integer(c_size_t), value, intent(in) :: n
     end subroutine bar
  end interface
end module foo

Your C function then would be

void bar(double *a, size_t n)

Edit:

The way to call your C function from Fortran would then be

program main
  use iso_c_binding
  use foo
  real(c_double), dimension(10) :: a
  call bar(a,size(a,kind=c_size_t))
  print *,a
end program main

Edit 2:

If you really want to do copy-in / copy-out each time, you can do something like

  subroutine bar2(array)
    real(kind=c_double), intent(inout), dimension(:) :: array
    real(kind=c_double), dimension(size(array)) :: a
    a = array  ! Copy in
    call bar(a,size(a,kind=c_size_t))
    array = a  ! Copy out
  end subroutine bar2
end module foo

But I am at a loss to understand why this would be necessary.

Edit 3:

If you are afraid of a mismatch between C and Fortran datatypes, you can write a generic wrapper to get around this. This is what it could look like:

module foo
  use iso_c_binding
  implicit none
  private
  public :: bar
  interface
     subroutine bar_double(a,n) bind(C)
       import
       real(kind=c_double), dimension(*), intent(inout) :: a
       integer(c_size_t), value, intent(in) :: n
     end subroutine bar_double
  end interface

  interface
     subroutine bar_float(a,n) bind(C)
       import
       real(kind=c_float), dimension(*), intent(inout) :: a
       integer(c_size_t), value, intent(in) :: n
     end subroutine bar_float
  end interface

  interface bar
     module procedure bar_aux_double, bar_aux_float
  end interface bar
contains
  subroutine bar_aux_double (a)
    real(kind=c_double), dimension(:), intent(inout) :: a
    call bar_double (a, size(a,kind=c_size_t))
  end subroutine bar_aux_double

  subroutine bar_aux_float (a)
    real(kind=c_float), dimension(:), intent(inout) :: a
    call bar_float (a, size(a,kind=c_size_t))
  end subroutine bar_aux_float
end module foo

Your main program then could look like

program main
  use foo
  integer, parameter :: dp = selected_real_kind(15)
  integer, parameter :: sp = selected_real_kind(6)
  real(dp), dimension(10) :: a_dp
  real(sp), dimension(10) :: a_sp
  call bar(a_dp)
  call bar(a_sp)
  print *,a_dp,a_sp
end program main

where you don't make any reference to iso_c_binding at all. If there is no wrapper function for dp or sp, compilation will fail for lack of a generic procedure.

Problem

I have a large existing Fortran95 code. It uses ``` real(dp), dimension(num) :: array ``` to declare arrays. I want to join in some C code and found that I can do this by writing interfaces to the C-functions and declaring arrays as ``` use iso_c_binding real(c_double), allocatable, target :: array(:) ``` I have working fortran functions which call the C-functions as ``` call myfunction(c_loc(array)); ``` What is needed to pass the `real(dp)` array to myfunction? Apparently, I would need to make a C-pointer from it (how?). Is there any other way than copying the array? Is it possible to ensure that both types indeed refer to compatible double precision data blocks? Most importantly the solution must work with the GNU compilers. Note, that replacing `real(dp)` with `real(c_double)` everywhere in the existing Fortran code is not an option for me right now. In case there is no alternative to copying the entire array, how would I do this properly in the interface?

Original source