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?
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).
来源:https://stackoverflow.com/questions/28697754/fortran-c-interoperability-and-float-arrays