考虑这个C-可互操作的Fortran子例程,它是从Python调用的,以Python回调函数作为输入参数,然后调用它
module FortranFunc_mod
! C-interoperable interface for the python callback
abstract interface
function getSquare_proc( x ) result(xSquared) bind(C)
use, intrinsic :: iso_c_binding, only: c_double
real(c_double), intent(in) :: x
real(c_double) :: xSquared
end function getSquare_proc
end interface
contains
subroutine fortranFunc( getSquareFromPython ) bind(C, name="fortranFunc")
!DEC$ ATTRIBUTES DLLEXPORT :: fortranFunc
use, intrinsic :: iso_c_binding, only: c_funptr, c_f_procpointer, c_double
implicit none
type(c_funptr), intent(in) :: getSquareFromPython
procedure(getSquare_proc), pointer :: getSquare
real(c_double) :: x = 2._c_double, xSquared
! associate the input C procedure pointer to a Fortran procedure pointer
call c_f_procpointer(cptr=getSquareFromPython, fptr=getSquare)
xSquared = getSquare(x)
write(*,*) "xSquared = ", xSquared
end subroutine fortranFunc
end module FortranFunc_mod
Python函数可以如下所示:
import numpy as np
import ctypes as ct
# import dll and define result type
ff = ct.CDLL('FortranFunc_mod')
ff.fortranFunc.restype = None
# define and decorate Python callback with propoer ctypes
@ct.CFUNCTYPE( ct.c_double, ct.c_double ) # result type, argument type
def getSquareFromPython(x): return np.double(x**2)
# call Fortran function
ff.fortranFunc( getSquareFromPython )
但是,使用ifort编译此代码(已成功完成),然后运行Python代码会导致以下错误
---------------------------------------------------------------------------
OSError Traceback (most recent call last)
<ipython-input-3-2b798bfb58b5> in <module>
11
12 # call Fortran function
---> 13 ff.fortranFunc( getSquareFromPython )
OSError: exception: access violation reading 0xFFFFFFFFFFFFFFFF
在这个简单的例子中,我遗漏了什么?在Fortran和python代码之间是否需要额外的C-wrapper来定义回调原型?如果您还可以提供与C等价的代码来调用Python函数,那也会有所帮助。你知道吗
示例的主要问题是
ff.fortranFunc
只指定了它的返回类型,而没有指定它的参数类型。Fortran子例程fortranFunc
有一个输入参数type(c_funptr)
,这也应该反映在Python方面。你知道吗具体如何实现一个解决方案,取决于您是只想在Python中进行更改,还是愿意在Fortran源代码中进行更改。我将概述这两种解决方案:
仅在Python中进行更改
下面是Python测试例程的更新版本(我称之为
test.py
),具体更改如下:ff.fortranFunc.argtypes
arg_type
被指定为指向c_double
的指针—标量参数在C中的传递方式getSquareFromPython
也被修改以反映这个x[0]
(有关最后两点的详细信息,请参见ctypes documentation-2.7版本可能会更清楚地解释这一点)
在Python和Fortran中进行更改
如果希望更接近原始的Python实现,也可以通过对
test.py
进行以下更改来实现:fortranFunc
的参数类型:arg_type = ct.c_double
getSquareFromPython
的返回值:x**2
但是,由于回调函数现在需要一个
c_double
作为输入参数(而不是指向该参数的指针),因此您必须更改Fortran抽象接口以反映这一点,方法是将value
属性添加到伪参数x
:编译并运行
编译并运行任何一个修改过的代码版本,在Windows上使用ifort得到以下结果(适当更改compile命令和库名称后,它也可以在Linux和OS X上使用gfortran):
注意两种情况之间的区别
这两个实现之间的差异通过查看
getSquareFromPython
的参数x
的动态类型可以清楚地反映出来(它还解释了两个备选方案所需的符号更改)。对于第一个备选方案,您可以将左侧的语句添加到getSquareFromPython
,以获得右侧显示的结果:而对于第二种选择:
相关问题 更多 >
编程相关推荐