У меня есть большой существующий код Fortran95. Оно использует
real(dp), dimension(num) :: array
объявлять массивы.
Я хочу присоединиться к некоторому C-коду и обнаружил, что могу это сделать, написав интерфейсы с C-функциями и объявив массивы как
use iso_c_binding real(c_double), allocatable, target :: array(:)
У меня есть функции fortran, которые называют C-функциями как
call myfunction(c_loc(array));
Что необходимо для передачи real(dp)
массива в myfunction? По-видимому, мне нужно будет сделать C-указатель (как?). Есть ли другой способ, чем копирование массива? Можно ли гарантировать, что оба типа действительно относятся к совместимым блокам данных с двойной точностью? Самое главное, что решение должно работать с компиляторами GNU. Обратите внимание, что замена real(dp)
real(c_double)
везде в существующем коде Fortran для меня сейчас не является вариантом.
Если нет альтернативы копированию всего массива, как бы я сделал это правильно в интерфейсе?
Во-первых, я предполагаю, что вы определяете dp как параметр в модуле где-то. Вы можете просто использовать
integer, parameter :: dp = c_double
в этом модуле (и if (dp /= c_double) stop "Bletchful sytem"
где-то.
Передача массива между C и Fortran работает следующим образом:
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
Ваша функция C тогда будет
void bar(double *a, size_t n)
Редактировать:
Способ вызова вашей функции C из Fortran был бы тогда
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
Изменить 2:
Если вы действительно хотите копировать / копировать каждый раз, вы можете сделать что-то вроде
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
Но я не понимаю, зачем это нужно.
Редактировать 3:
Если вы боитесь несоответствия между типами данных C и Fortran, вы можете написать общую оболочку, чтобы обойти это. Вот как это могло бы выглядеть:
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
Тогда ваша основная программа могла бы выглядеть
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
где вы вообще не ссылаетесь на iso_c_binding. Если для dp или sp нет функции-обертки, компиляция завершится неудачей из-за отсутствия общей процедуры.
Если вы используете модули, не беспокойтесь слишком много, смешивая dp
и c_double
внутри Fortran. В очень маловероятном случае, когда selected_real_kind(15, 307) /= c_double
компилятор будет жаловаться при проверке интерфейсов процедуры. В противном случае он увидит, что номера номеров согласуются, и все равно, как вы называете константу вида (кроме случаев, когда объявляются совместимые процедуры).