#fortran #fortran-iso-c-binding
#fortran #fortran-iso-c-binding
Вопрос:
Скажем, у меня есть функции C , которые я должен реализовать в библиотеке Fortran.
Я решил создать свою оболочку Fortran в модуле, который выглядит так, как показано ниже.
(Сторона C )
extern "C" void cpp_func1(int* n, int* array) { ... }
(Сторона Fortran)
module cpp2fortran
use ISO_C_BINDING
implicit none
interface
! (1)
subroutine cpp_func1(n, array) bind(C)
use iso_c_binding
integer(C_INT) , intent(in) :: n
type(C_PTR) , value, intent(in) :: array
end subroutine
end interface
contains
! (2)
subroutine Utilize_cpp_func1( ... ) ! bind(C)
use iso_c_binding
...
call cpp_func1(...)
...
end subroutine
end module
Я пометил числа в двух местах, например, (1) и (2) .
В конце (1) я определенно ставлю bind(C)
, и без этого код не будет скомпилирован.
Дело в том, что я должен поставить bind(C)
в конце (2)?
Код компилируется / работает просто отлично, независимо от наличия bind(C)
в конце строки (2), но я обнаружил, что некоторая оптимизация, подобная Fortran, не работает, если я помещаю bind(C)
.
Например, в обычном Fortran без bind(C)
,
real, allocatable :: arr1(:)
real, allocatable :: arr2(:)
...
arr2 = arr1
...
будет работать нормально. Однако, если я помещу bind(C)
, это не сработает, и мне пришлось внести некоторые изменения, такие как следующие
real, allocatable :: arr1(:)
real, allocatable :: arr2(:)
...
do i = 1, size
arr2(i) = arr1(i)
enddo
...
Если безопасно удалить bind(C)
из (2)-подобной оболочки Fortran, я был бы рад удалить ее и использовать краткую оптимизацию, например, arr2 = arr1
вместо того, чтобы создавать длинный код, пишущий все циклы выполнения.
Это минимальный пример, в котором bind(C)
возникает проблема. Я использовал ifort для ее компиляции.
module cbind
use iso_c_binding
implicit none
contains
(*) subroutine sub1(array) !bind(C)
use iso_c_binding
implicit none
real, allocatable, intent(in) :: array(:)
real(C_FLOAT), allocatable, target :: array_target(:)
allocate( array_target(size(array,1)) )
array_target = array
print *, 'array'
print *, array
print *, 'array_target'
print *, array_target
end subroutine
end module
program test
use iso_c_binding
use cbind
implicit none
real, allocatable :: array(:)
allocate( array(5) )
array = 1.0
call sub1(array)
end program
Без bind(C)
строки (*) код просто отлично работает с желаемым результатом.
array
1.000000 1.000000 1.000000 1.000000 1.000000
array_target
1.000000 1.000000 1.000000 1.000000 1.000000
Однако, если я помещу bind(C)
, код завершится следующим выводом.
array
1.000000 1.000000 1.000000 1.000000 1.000000
array_target
forrtl: severe (174): SIGSEGV, segmentation fault occurred
Image PC Routine Line Source
cbinding1 00000000004051B3 Unknown Unknown Unknown
libpthread-2.17.s 00002AC983A5E5F0 Unknown Unknown Unknown
cbinding1 000000000045382A Unknown Unknown Unknown
cbinding1 000000000042B967 Unknown Unknown Unknown
cbinding1 000000000040FCB3 Unknown Unknown Unknown
cbinding1 000000000040CA01 Unknown Unknown Unknown
cbinding1 0000000000403C1B Unknown Unknown Unknown
cbinding1 00000000004037E2 Unknown Unknown Unknown
libc-2.17.so 00002AC983C8D505 __libc_start_main Unknown Unknown
cbinding1 00000000004036E9 Unknown Unknown Unknown
Комментарии:
1. Вам нужно
Utilize_cpp_func1
быть совместимым с C? (Что касаетсяarr2 = arr1
,bind(c)
это не должно иметь никакого значения. Если вас беспокоит эта часть, пожалуйста, дайте более подробную информацию.)2. @francescalus Нет, я буду использовать
Utilize_cpp_func1
только на стороне Fortran. И я отредактировал свой вопрос с минимальным кодом, который демонстрирует ситуацию. Спасибо 🙂
Ответ №1:
Атрибут BIND для процедуры необходим, чтобы сделать ее совместимой с C. Хотя у него есть другие эффекты, атрибут не является необходимым для процедуры Fortran, чтобы иметь возможность использовать саму совместимую процедуру. Более конкретно, процедура, которая не совместима, может использовать не совместимую процедуру: вероятно, этот очень полезный факт часто используется.
В вашем случае (из того, что мы видим) вам не нужно bind(c)
определение Utilize_cpp_func1
. Сама эта процедура не требуется для совместимости с C.
Что касается беспокойства array_target = array
, это просто слабость компилятора в некоторых версиях компилятора Intel. Его нет в текущей версии.
Почему это bind(c)
имеет значение в этом примере? Ваш фиктивный аргумент array
является распределяемым. Совместимая процедура может иметь выделяемый фиктивный аргумент только в Fortran 2008 TS29113 или Fortran 2018, а не в Fortran 2003 или Fortran 2008: это относительно новая функция языка и подвержена ошибкам в ее реализации (или при использовании интерпретаций языка, которые не позволяют этогоособенность).
Комментарии:
1. Тогда, даже если мой компилятор, похоже, выполняет неправильную работу в каком-то аспекте, я могу придерживаться его, удалив
bind(C)
в этом случае? Мне нужно использовать компилятор Intel с разрядным порядком (v2018), поскольку библиотека линейных решателей, которую я использую в настоящее время, имеет некоторые проблемы с v2019 и так далее.2. Как общее правило, я бы сказал: не делайте процедуру (или что-либо еще) совместимой, если вам это не нужно. Удаление
bind(c)
здесь имеет смысл.