C回调函数的抽象Fortran接口是否需要bind(C)属性? [英] Does the abstract Fortran interface of a C callback function require bind(C) attribute?

查看:173
本文介绍了C回调函数的抽象Fortran接口是否需要bind(C)属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下Fortran代码,其中模块Foo_mod中可与C互操作的子例程runFoo4C(...) bind(C, name="runFoo")将C回调函数指针getLogFuncFromC()作为参数,

Consider the following Fortran code, where the C-interoperable subroutine runFoo4C(...) bind(C, name="runFoo") in module Foo_mod takes a C-callback function pointer getLogFuncFromC() as an argument,

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接口由上述代码中的模块CallbackInterface_mod中的getLogFunc4C_proc()给出.现在的问题:

The abstract Fortran interface of this callback function, is given by getLogFunc4C_proc() in module CallbackInterface_mod in the above code. Now the question:

此抽象接口是否需要bind(c)属性才能符合fortran标准?我自己的天真猜测是,它不需要bind(c),因为它不会在接口中使用函数的全局标识符来调用,但是抽象接口只是确定了C回调函数的接口,即指向传递给Fortran,以便稍后从Fortran内部调用.

Does this abstract interface require a bind(c) attribute to comply with the fortran standard? My own naive guess is that it does not need the bind(c) as it is not going to be called with the global identifier of the function in the interface, but the abstract interface simply determines the interface of the C callback function, a pointer to which is passed to Fortran to be called later from inside Fortran.

实际上,使用ifort(18.0.2 Windows编译器)在抽象界面中对此bind(c)属性进行注释不会导致任何编译或运行时错误.

Indeed, commenting out this bind(c) attribute in the abstract interface does not lead to any compilation or runtime error using ifort (18.0.2 Windows compiler).

如果不需要,那么该抽象接口中的变量声明又如何呢?是否需要通过iso_c_binding内在模块中的C兼容类声明它们?

If it is not needed, then how about variable declarations in this abstract interface? Do they need to be declared by C-conforming kinds from the iso_c_binding intrinsic module?

推荐答案

抽象接口中BIND(C)的存在(或不存在)会更改过程指针的特性,但这样做的方式是该程序不会不透露.因为您通过从C_FUNPTR转换而来的指针对getLogFunc进行了调用,所以可以防止编译器在抽象接口中忽略BIND(C)时不会注意到不匹配.例如,如果该过程具有character(*)参数,则可能由于不匹配而发生很多不好的事情.

The presence (or absence) of BIND(C) in the abstract interface changes the characteristics of the procedure pointer, but does so in a way that this program doesn't reveal. Because you do the call to getLogFunc through a pointer you converted from a C_FUNPTR, you prevent the compiler from noticing the mismatch should BIND(C) be omitted in the abstract interface. For example, should the procedure have a character(*) argument, lots of bad things would happen for a mismatch.

BIND(C)本身就可以在抽象界面中使用.由于它会更改过程的调用方式,因此如果被调用过程是可互操作的,则必须指定它.

BIND(C), by itself, is fine in an abstract interface, as long as you don't also say NAME=. Since it changes how the procedure is called, you must specify it if the called procedure is interoperable.

关于如果不需要,那么在这个抽象接口中如何进行变量声明?它们是否需要通过iso_c_binding内部模块的C兼容类型进行声明?",您会遇到常见错误,即在内部函数中合并定义ISO_C_BINDING模块具有可互操作性.该模块中的种类常数只是数字,它们没有什么神奇的.您需要使实际参数和虚拟参数的类型,种类和等级匹配(某些例外).

Regarding "If it is not needed, then how about variable declarations in this abstract interface? Do they need to be declared by C-conforming kinds from the iso_c_binding intrinsic module?", you make the common error of conflating definitions in intrinsic module ISO_C_BINDING with being interoperable. The kind constants in that module are just numbers, there's nothing magical about them. You are required to have the actual and dummy arguments match in type, kind and rank (with some exceptions.)

这篇关于C回调函数的抽象Fortran接口是否需要bind(C)属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆