Требует ли абстрактный интерфейс Fortran функции обратного вызова C атрибута bind (C)? - PullRequest
0 голосов
/ 26 февраля 2019

Рассмотрим следующий код на Фортране, где C-совместимая подпрограмма runFoo4C(...) bind(C, name="runFoo") в модуле Foo_mod принимает в качестве аргумента указатель на функцию C-callback getLogFuncFromC(),

module CallbackInterface_mod

    abstract interface
        function getLogFunc4C_proc(ndim,Point) result(logFunc) ! bind(C)
            use, intrinsic :: iso_c_binding, only : c_int32_t, c_double, c_int
            integer(c_int32_t), intent(in)  :: ndim
            real(c_double), intent(in)      :: Point(ndim)
            real(c_double)                  :: logFunc
        end function getLogFunc4C_proc
    end interface

end module CallbackInterface_mod

!***********************************************************************************************************************************
!***********************************************************************************************************************************

module Foo_mod

    interface
    module subroutine runFoo4C(ndim, getLogFuncFromC, inputString, inputStringLen) bind(C, name="runFoo")
        use, intrinsic :: iso_c_binding, only: c_int32_t, c_char, c_funptr, c_f_procpointer, c_size_t
        use CallbackInterface_mod, only: getLogFunc4C_proc
        implicit none
        integer(c_int32_t) , intent(in)                         :: ndim
        character(len=1, kind=c_char), dimension(*), intent(in) :: inputString
        integer(c_size_t) , intent(in)                          :: inputStringLen
        type(c_funptr), intent(in), value                       :: getLogFuncFromC
    end subroutine runFoo4C
    end interface

contains

    subroutine runFoo(ndim, getLogFunc, string)
        !use CallbackInterface_mod, only: getLogFunc_proc
        use CallbackInterface_mod, only: getLogFunc4C_proc
        use, intrinsic :: iso_fortran_env, only: RK => real64
        implicit none
        integer :: ndim
        procedure(getLogFunc4C_proc)    :: getLogFunc
        character(*), intent(in)        :: string
        real(RK)                        :: Point(ndim)
        character(:), allocatable       :: mystring
        Point = [1._RK,1._RK]
        write(*,*) "Hi again, this is a call from inside runFoo!"
        write(*,*) "getLogFunc(2,[1,1]) = ", getLogFunc(ndim,Point)
        write(*,*) "string = ", string
    end subroutine

end module Foo_mod

!***********************************************************************************************************************************
!***********************************************************************************************************************************

submodule (Foo_mod) Foo_smod

contains

    module subroutine runFoo4C(ndim, getLogFuncFromC, InputString, inputStringLen) bind(C, name="runFoo")

        use, intrinsic :: iso_c_binding, only: c_double, c_int32_t, c_char, c_funptr, c_f_procpointer, c_size_t
        use CallbackInterface_mod, only: getLogFunc4C_proc
        implicit none
        integer(c_int32_t) , intent(in)                         :: ndim
        character(len=1, kind=c_char), dimension(*), intent(in) :: InputString
        integer(c_size_t) , intent(in)                          :: inputStringLen
        type(c_funptr), intent(in), value                       :: getLogFuncFromC
        procedure(getLogFunc4C_proc), pointer                   :: getLogFunc
        real(c_double)                                          :: Point(ndim)
        character(:), allocatable                               :: inputString4tran
        integer                                                 :: i

        write(*,*) "InputString: ", InputString(1:inputStringLen)
        allocate( character(len=inputStringLen) :: inputString4tran )
        do i=1,inputStringLen
            inputString4tran(i:i) = InputString(i)
        end do
        write(*,*) "inputString4tran: ", inputString4tran

        ! associate the input C procedure pointer to a Fortran procedure pointer
        call c_f_procpointer(cptr=getLogFuncFromC, fptr=getLogFunc)
        Point = [1._c_double, 1._c_double]
        write(*,*) "Here we go: "
        write(*,*) "getLogFunc(ndim=2, [1._c_double, 1._c_double]): ", getLogFunc( ndim, Point )

        call runFoo(ndim, getLogFunc, inputString4tran)

    end subroutine runFoo4C

end submodule Foo_smod

Абстрактный интерфейс Fortranэтой функции обратного вызова, задается getLogFunc4C_proc() в модуле CallbackInterface_mod в приведенном выше коде.Теперь вопрос:

Требуется ли для этого абстрактного интерфейса атрибут bind(c) для соответствия стандарту Fortran?Мое собственное наивное предположение состоит в том, что ему не нужен bind(c), так как он не будет вызываться с глобальным идентификатором функции в интерфейсе, но абстрактный интерфейс просто определяет интерфейс функции обратного вызова C, указатель накоторый передается в Fortran для последующего вызова изнутри Fortran.

Действительно, комментирование этого атрибута bind(c) в абстрактном интерфейсе не приводит к какой-либо ошибке компиляции или времени выполнения при использовании ifort (18.0.2 Windows compiler).

Если это не нужно, то как насчет объявлений переменных в этом абстрактном интерфейсе?Должны ли они быть объявлены C-совместимыми видами из встроенного модуля iso_c_binding?

Ответы [ 2 ]

0 голосов
/ 26 февраля 2019

Наличие (или отсутствие) BIND (C) в абстрактном интерфейсе изменяет характеристики указателя процедуры, но делает это таким образом, что эта программа не раскрывает.Поскольку вы выполняете вызов getLogFunc через указатель, преобразованный из C_FUNPTR, вы не позволяете компилятору замечать несоответствие, если BIND (C) опускается в абстрактном интерфейсе.Например, если процедура имеет аргумент символ (*), много несоответствий может произойти из-за несоответствия.

BIND (C), сам по себе, подходит для абстрактного интерфейса, если вы не используететакже не говорите ИМЯ =.Так как он меняет способ вызова процедуры, вы должны указать его, если вызываемая процедура совместима.

Относительно "Если это не нужно, то как насчет объявлений переменных в этом абстрактном интерфейсе? Нужно ли их объявлять?с помощью C-совместимых видов из встроенного модуля iso_c_binding? ", вы допускаете общую ошибку при сопоставлении определений во встроенном модуле ISO_C_BINDING с возможностью взаимодействия.Добрые константы в этом модуле - просто числа, в них нет ничего волшебного.Вам необходимо, чтобы фактические и фиктивные аргументы совпадали по типу, виду и рангу (за некоторыми исключениями.)

0 голосов
/ 26 февраля 2019

Спецификация c_f_procpointer отличается в Fortran 2008 и Fortran 2018. В любом случае, давайте посмотрим на оператор

call c_f_procpointer(cptr=getLogFuncFromC, fptr=getLogFunc)

В Fortran 2008 необходимо, чтобы интерфейс для аргумента fptr былсовместим с целью аргумента cptr.Для обеспечения взаимодействия интерфейс должен иметь атрибут bind.

В Fortran 2018 это требование смягчено, но нам разрешено иметь не взаимодействующий интерфейс для аргумента fptr только тогда, когда cptrАргумент является результатом ссылки на c_funloc.Это может произойти в зависимости от того, как вызывается runFoo4C.

В любом случае компилятору не требуется диагностировать любое нарушение требований (и в последнем случае легко увидеть, какхитро, что может быть).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...