Fortran-C interoperability and float arrays

走远了吗. 提交于 2019-12-03 20:50:05

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.

If you use modules, don't worry too much when mixing dp and c_double inside Fortran. In the very unlikely case that selected_real_kind(15, 307) /= c_double the compiler will complain when checking the procedure interfaces. Otherwise, it will see that the kind numbers agree and it does not care how you call the kind constant (except when declaring interoperable procedures).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!