Интерфейс Fortran to C для массивов и скаляров

Я изучаю, как связать Fortran с C. Я написал простой код C, который устанавливает все значения массива в 1:

#include <stdlib.h>
void cset( int *array, int size ) {
  for (size_t i = 0; i < size; i++) {
    array[i] = 1;
  }
  return;
}

Я хотел написать интерфейс Fortran для работы как с массивами (любой размерности), так и со скалярами.
Мой код на Fortran:

program main
  use iso_c_binding
  implicit none

  interface cset
    ! Interface for arrays.
    subroutine cset_array( array, size ) bind( C, NAME="cset" )
      import
      integer(C_INT), intent(inout)     :: array(*)
      integer(C_INT), value, intent(in) :: size
    end subroutine cset_array

    ! Interface for scalars. NOTE: Fortran values are passed by reference (so this should work)
    subroutine cset_scalar( scalar, size ) bind( C, NAME="cset" )
      import
      integer(C_INT), intent(inout)     :: scalar
      integer(C_INT), value, intent(in) :: size
    end subroutine cset_scalar

  end interface cset

  integer :: scalar = 0
  integer :: array(3) = 0
  integer :: array2d(3,3) = 0

  call cset( scalar, 1 )
  print *, scalar

  call cset( array, size(array) )
  print *, array

  ! Does not work???
  ! call cset( array2d, size(array2d) )

  ! But works for
  call cset_array( array2d, size(array2d) )
  ! OR call cset( array2d(1,1), size(array2d) )
  print *, array2d

end program main

Это прекрасно работает для скаляров и одномерных массивов.

Почему интерфейс не работает для cset( array2d, size(array2d) ), но работает для cset_array( array2d, size(array2d) ) или call cset( array2d(1,1), size(array2d) )? В первом случае я получаю следующую ошибку (gfortran-7.4):

call cset( array2d, size(array2d) )
                                  1
Error: There is no specific subroutine for the generic ‘cset’ at (1)

Есть ли более «правильный» способ написания такого интерфейса? Можно ли вообще передавать скаляр таким образом?

Спасибо и с уважением.

СВЯЗАННЫЕ:
Передача двумерного массива из Fortran в C
Передача как скаляры, так и массивы (любой размерности) от Fortran до C


person jcerar    schedule 09.01.2020    source источник


Ответы (1)


Вы не можете использовать массивы предполагаемого размера (dimension(*)), чтобы разрешить использование одной и той же конкретной процедуры для массивов нескольких рангов (размеров). Из-за правил TKR (тип, вид, ранг) все определенные процедуры предназначены только для одного ранга. Если вы попытаетесь передать другой массив рангов универсальной процедуре, конкретная процедура не будет распознана, даже если можно напрямую передать массив в конкретную процедуру.

Единственное решение, которое я знаю, это создавать определенные интерфейсы процедур для каждого ранга отдельно.

subroutine cset_array1( array, size ) bind( C, NAME="cset" )
  import
  integer(C_INT), intent(inout)     :: array(*)
  integer(C_INT), value, intent(in) :: size
end subroutine cset_array1

subroutine cset_array2( array, size ) bind( C, NAME="cset" )
  import
  integer(C_INT), intent(inout)     :: array(1,*)
  integer(C_INT), value, intent(in) :: size
end subroutine cset_array2

Если у вас есть только одна процедура, которая может принимать что угодно, например процедуры MPI, можно использовать специфичный для компилятора

!$GCC attributes no_arg_check

или то же самое с !$DEC

person Vladimir F    schedule 09.01.2020
comment
Как насчет фиктивных аргументов предполагаемого ранга Fortran 2018? - person King; 11.01.2020
comment
@King Аргументы предполагаемого ранга передаются с использованием дескриптора, и для его понимания требуется специальный код на стороне C. Он не совместим с простыми указателями массива C. - person Vladimir F; 11.01.2020