Вызов подпрограмм Fortran с необязательными аргументами из C++

Как мне сослаться на функцию Fortran в заголовке C++, который использует необязательные аргументы? Буду ли я иметь прототип в заголовке для каждой возможной комбинации вызовов? Или это вообще возможно?

Например, Фортран:

subroutine foo(a, b, c) bind(c)
   real, intent(in), optional :: a, b, c
   ...
end subroutine foo

person DavidH    schedule 17.10.2016    source источник


Ответы (2)


Это невозможно, по крайней мере, переносимо, если вы не сделаете подпрограмму bind(C).

Как только вы сделаете это bind(C), это просто передача указателя, который может быть NULL на стороне C.

subroutine foo(a, b, c) bind(C, name="foo")
   real, intent(in), optional :: a, b, c
   ...
end subroutine foo

(для большей переносимости следует использовать real(c_float) из модуля iso_c_binding, но это несколько касается этого вопроса)

In C(++)

extern "C"{
  void foo(float *a, float *b, float *c);
}

foo(&local_a, NULL, NULL);

а затем вы можете создать функцию C++, которая вызывает foo и использует необязательные параметры в стиле C++.

Эта возможность была разрешена в Fortran в Технической спецификации ISO/IEC TS 29113:2012 по дальнейшему взаимодействию Fortran с C.

person Vladimir F    schedule 17.10.2016
comment
Действительно, без bind(c) это никуда не денется. Спасибо! - person DavidH; 17.10.2016

Как Владимир Ф. отвечает, в Fortran 2018 (и Fortran 2008+TS29113) можно использовать атрибут optional для фиктивных аргументов в совместимой с C процедуре Fortran.

В Fortran 2008 это невозможно. Некоторые компиляторы до сих пор не поддерживают эту функцию. С этими компиляторами все еще (хотя и с дополнительной работой) можно поддерживать «необязательные» аргументы.

Процедура foo вопроса не совместима с C под F2008 (даже с bind(C)). Однако в F2008 можно подражать этой идее: иметь совместимую с C процедуру с type(c_ptr) аргументами, которая обертывает желаемую процедуру Fortran. Эта интероперабельная оболочка может проверять наличие нулевых указателей (используя C_ASSOCIATED), чтобы определить, присутствуют ли переданные далее аргументы или нет, и передавать разыменованные аргументы, если это так.

Например, сторона Fortran с оболочкой, совместимой с C, может выглядеть так:

module mod

  use, intrinsic :: iso_c_binding

contains

  subroutine foo_centry(a) bind(c,name='foo')
    type(c_ptr), value :: a
    real(c_float), pointer :: a_pass

    nullify(a_pass)
    if (c_associated(a)) call c_f_pointer(a, a_pass)
    call foo(a_pass)
  end subroutine foo_centry

  subroutine foo(a)
    real(c_float), optional :: a
  end subroutine foo

end module mod

В Fortran 2018 у нас есть эта симметрия в интероперабельном интерфейсе: если процедура определена средствами, отличными от Fortran, но интероперабельный интерфейс имеет необязательный аргумент, то в F2018 мы получаем результат, что ссылка на эту процедуру с отсутствующим аргументом означает, что процедуре передается нулевой указатель.

В F2008 нам нужно обрабатывать и эту сторону: мы снова делаем это с неинтероперабельной процедурой F2008, которая обертывает интероперабельную процедуру с type(c_ptr) аргументами: если аргумент присутствует, передать его адрес; если нет, пройдите C_NULL_PTR.

Такой код F2008 может выглядеть как

module mod
  use, intrinsic :: iso_c_binding

  interface
     subroutine foo_fentry(a) bind(c,name='foo')
       import c_ptr
       type(c_ptr), value :: a
     end subroutine foo_fentry
  end interface

contains

  subroutine foo(a)
    real(c_float), optional, target :: a

    if (present(a)) then
       call foo_fentry(c_loc(a))
    else
       call foo_fentry(c_null_ptr)
    end if
  end subroutine foo

end module mod

Помните об ограничениях, вызванных использованием c_loc: в некоторых случаях может потребоваться использовать копию или принять другие защитные меры.

person francescalus    schedule 30.07.2019
comment
Большое спасибо за это исчерпывающее объяснение и подробные сведения о том, как это сделать с помощью компилятора F2008. Чтобы быть уверенным, как это сделать с помощью F2008: если я хочу вызвать функцию C foo_c(int* val) из Фортрана с поддержкой необязательных аргументов, я сначала создам подпрограмму foo_f(val) bind(c, name='foo_c') с type(c_ptr), value, intent(in) :: val. Подпрограмма-оболочка тогда foo_f_wrap(val) с integer, intent(in), target, optional :: val и либо вызывает fun_f(c_loc(val)), если val присутствует, либо fun_f(c_null_ptr), если нет. Я правильно понял? - person Michael Schlottke-Lakemper; 31.07.2019
comment
Я добавил примеры, по одному для каждого направления обертывания. - person francescalus; 31.07.2019
comment
Идеальный. Я думаю, что это действительно проясняет для всех, кто смотрит на этот вопрос и рассматривает направления вызовов как C->F, так и F->C. - person Michael Schlottke-Lakemper; 01.08.2019