Должен ли я поместить ‘bind (C)’ рядом с подпрограммой, которая не определена на стороне C?

#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) здесь имеет смысл.