Fortran-C Compatibility Interfaces and Floating Arrays

I have a large existing Fortran95 code. He uses

real(dp), dimension(num) :: array 

declare arrays.

I want to join some C code and found that I can do this by writing interfaces with C functions and declaring arrays as

 use iso_c_binding real(c_double), allocatable, target :: array(:) 

I have fortran functions that call C functions like

 call myfunction(c_loc(array)); 

What is needed to pass a real(dp) array to myfunction? Apparently I will need to make a C pointer (how?). Is there any other way than copying an array? Is it possible to guarantee that both types really belong to compatible data blocks with double precision? Most importantly, the solution should work with GNU compilers. Note that replacing real(dp) with real(c_double) everywhere in existing Fortran code is not an option for me right now.

If there is no alternative to copying the entire array, how would I do it right in the interface?

+2
source share
2 answers

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

 integer, parameter :: dp = c_double 

in this module (and where if (dp /= c_double) stop "Bletchful sytem" .

Array transfer between C and Fortran works as follows:

 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 function c will

 void bar(double *a, size_t n) 

Edit:

A way to call your C function from Fortran would 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 copy / copy every 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 do not understand why this is necessary.

Edit 3:

If you are afraid of a mismatch between the C and Fortran data types, you can write a generic wrapper to get around this. Here's what it looks 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 would then 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 refer to iso_c_binding at all. If there is no wrapper function for dp or sp, compilation will fail due to the lack of a general procedure.

+4
source

If you use modules, don't bother mixing dp and c_double inside Fortran. In the very unlikely event that selected_real_kind(15, 307) /= c_double compiler will complain when checking the interfaces of the procedure. Otherwise, he will see that the type numbers are consistent, and it doesnโ€™t matter what you call the view constant (except when compatible procedures are declared).

+1
source

Source: https://habr.com/ru/post/1239645/


All Articles