Fortran C—具有bind(C)的可互操作子模块过程在由gfortran编译时报告错误

Fortran C—具有bind(C)的可互操作子模块过程在由gfortran编译时报告错误,fortran,gfortran,intel-fortran,fortran-iso-c-binding,Fortran,Gfortran,Intel Fortran,Fortran Iso C Binding,考虑以下Fortran模块Foo_mod及其子模块Foo_smod 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 i

考虑以下Fortran模块
Foo_mod
及其子模块
Foo_smod

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: 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(*,*) "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模块,它确实通过英特尔Fortran编译器2018在Windows和Linux上成功编译并链接到C程序,并且运行正常。但是,当由gfortran 7.3.0编译时,会出现以下错误:

gfortran -c ../Foo_mod.f90
../Foo_mod.f90:54:18:

submodule (Foo_mod) Foo_smod
                1
Error: BIND(C) attribute at (1) can only be used for variables or common blocks
如果我从模块
Foo_mod
中的可互操作子例程
runFoo4C()
的接口中删除
bind(c)
属性,则问题可以得到解决,如下所示:

...
module subroutine runFoo4C(ndim, getLogFuncFromC, inputString, inputStringLen) ! bind(C, name="runFoo")
...

然而,“英特尔Fortran编译器”随后会抱怨模块
Foo_mod
中的子例程接口与子模块
Foo_smod
中的子例程实现不兼容。在我看来,这更像是GFORTRAN中的一个bug,而不是编程错误。但在我继续报告之前,非常感谢您的评论。

我很清楚,这是一个与子模块相关的gfortran错误。由于这是一个相当新的功能添加,所以这里和那里可能存在bug也就不足为奇了


我要说的是,在这里问这样的问题时,强烈建议您提供一个问题,因为您没有显示子模块附带的模块,我们无法在没有不必要的额外工作的情况下自己验证问题。

嗨,Steve,感谢您确认错误。父模块
Foo_mod
实际上是在我的问题中给出的,就在子模块声明之前。将这里给出的整个代码复制并粘贴到一个类似
Foo_mod.f90
的文件中,并编译它以查看错误就足够了。但有一件事我还没有检查,那就是这个bug是否在较新的GFORTRAN版本中仍然存在。但我目前还没有访问更新版本的权限。如果gfortran 7.3,我使用的版本。0@King关键是子例程runFoo是否具有与为runFoo4C创建的“绑定标签”相同的“绑定标签”。该标准规定“绑定标签不应与任何其他全局实体的全局标识符相同,忽略大小写差异。”(F2018,19.2p2)模块过程runFoo不太可能具有绑定标签“runFoo”(加上配套C编译器将添加的任何装饰)。例如,在Windows上的英特尔Fortran中,runFoo将是FOO_MOD_mp_RUN_FOO。也就是说,这样做不是一个好主意。还要注意,模块过程的名称不是全局标识符,否则在任何其他模块中都不能有runFoo。但是请注意,您通常可以构造一个复制另一个过程的绑定标签,这将使程序无效。第二个定义了一个通用名称;我想您可以在泛型中有两个模块过程,但我看不出这有什么好处。您不希望Fortran代码使用C版本,并且必须有可区分的参数列表,这限制了您的使用。