python-fortran集成:f2py和ctypes之间的回调比较

问题描述 投票:1回答:1

[我在https://numpy.org/devdocs/f2py/python-usage.html#call-back-arguments中找到了一个有启发性的例子。这里是fortran例程:

C FILE: CALLBACK.F
  SUBROUTINE FOO(FUN,R)
  EXTERNAL FUN
  INTEGER I
  REAL*8 R, FUN
Cf2py intent(out) r
  R = 0D0
  DO I=-5,5
     R = R + FUN(I)
  ENDDO
  END
C END OF FILE CALLBACK.F

可以用命令&f2py -c -m callback callback.f编译。并使用python代码调用:

import callback
print(callback.foo.__doc__)
def f(i):
    return i * i
print(callback.foo(f))

一切正常。现在,我想使用ctypes重复测试。我可以使用以下命令轻松编译fortran源:gfortran -shared callback.f -o callback.dll我可以用以下方式加载库:

import ctypes as ct
import numpy as np
# import the dll
fortlib = ct.CDLL('callback.dll')

问题:

  • 我如何像我这样的ctypes调用dll中的foo函数用f2py编译的代码做了什么?
  • 我如何将两者连接是否需要变量(指向函数的指针和指向实数的指针)?

谢谢。 Gianmarco

平台:Anaconda python 3.7.6,在Windows 10上为Mingw-64

python-3.x fortran ctypes f2py
1个回答
0
投票

良好的编程风格指示我们从不使用单字符变量名称。经验丰富的现代Fortran程序员将实现类似于以下内容的Fortran函数:

module foo_mod

    use iso_c_binding, only: RK => c_double, IK => c_int32_t
    implicit none

    abstract interface
        function getFunVal_proc(inputInteger) result(funVal) bind(C)
            import :: RK, IK
            implicit none
            integer(IK), intent(in), value :: inputInteger
            real(RK) :: funVal
        end function getFunVal_proc
    end interface

contains

    subroutine getFoo(getFunValFromC,outputReal) bind(C,name="getFoo")
        !DEC$ ATTRIBUTES DLLEXPORT :: getFoo
        use, intrinsic :: iso_c_binding, only: c_funptr, c_f_procpointer
        implicit none
        type(c_funptr), intent(in), value   :: getFunValFromC
        procedure(getFunVal_proc), pointer  :: getFunVal
        real(RK), intent(out)               :: outputReal
        integer(IK)                         :: indx

        ! associate the input C procedure pointer to a Fortran procedure pointer
        call c_f_procpointer(cptr=getFunValFromC, fptr=getFunVal)

        outputReal = 0._RK
        do indx = -5,5
            write(*,"(*(g0,:,' '))") "value of indx from inside Fortran: ", indx
            outputReal = outputReal + getFunVal(indx)
        end do

        write(*,"(*(g0,:,' '))") "value of outputReal from inside Fortran: ", outputReal

        ! nullify the Fortran pointer
        nullify(getFunVal)

    end subroutine getFoo

end module foo_mod

这看起来很冗长,但是比F77好得多。毕竟,我们生活在21世纪。然后,您可以通过Intel ifort编译此Fortran代码,例如,

ifort /dll /threads /libs:static foo_mod.f90 /exe:foo.dll

然后,您将像下面的Python脚本一样从生成的DLL getFoo()中调用foo.dll,>

import ctypes as ct
import numpy as np

def getSquare(inputInteger):
    print("value of indx received by getSquare() inside Python: ",inputInteger)
    return np.double(inputInteger**2)

# define ctypes wrapper function, with the proper result and argument types
getFunVal_proc =    ct.CFUNCTYPE( ct.c_double                  # callback (python) function result
                                , ct.c_int32                   # callback (python) function input integer argument
                                )
getSquare_pntr = getFunVal_proc(getSquare)

libpath = "foo.dll"
try:

    foolib = ct.CDLL(libpath)

except Exception as e:

    import logging
    logger = logging.Logger("catch_all")
    logger.error(e, exc_info=True)

# define getFoo's interface from Fortran dll

foolib.getFoo.restype = None
foolib.getFoo.argtypes =    [ getFunVal_proc            # procedure
                            , ct.POINTER(ct.c_double)   # real64 return value
                            , ]

outputReal = ct.c_double(0.)
foolib.getFoo   ( getSquare_pntr
                , ct.byref(outputReal)
                )
print("value of outputReal received in Python: ", np.double(outputReal))

运行此脚本将产生类似以下的内容,

In [1]: run main.py
value of indx from inside Fortran:  -5
value of indx received by getSquare() inside Python:  -5
value of indx from inside Fortran:  -4
value of indx received by getSquare() inside Python:  -4
value of indx from inside Fortran:  -3
value of indx received by getSquare() inside Python:  -3
value of indx from inside Fortran:  -2
value of indx received by getSquare() inside Python:  -2
value of indx from inside Fortran:  -1
value of indx received by getSquare() inside Python:  -1
value of indx from inside Fortran:  0
value of indx received by getSquare() inside Python:  0
value of indx from inside Fortran:  1
value of indx received by getSquare() inside Python:  1
value of indx from inside Fortran:  2
value of indx received by getSquare() inside Python:  2
value of indx from inside Fortran:  3
value of indx received by getSquare() inside Python:  3
value of indx from inside Fortran:  4
value of indx received by getSquare() inside Python:  4
value of indx from inside Fortran:  5
value of indx received by getSquare() inside Python:  5
value of outputReal from inside Fortran:  110.0000000000000
value of outputReal received in Python:  110.0

与您的F2PY代码相比,该Python脚本可能看起来又很冗长。但是,与Python和Fortran标准相比,它比您的实现更加专业,现代且符合标准。

脚注:在Windows,Linux和Mac平台上,所有学生,教师和开源开发人员均可免费获得Intel ifort。这并不意味着gfortran不好。但是我认为,在Windows操作系统上使用gcc通常并不比永无止境的噩梦更好。

© www.soinside.com 2019 - 2024. All rights reserved.