短版
我有旧的Fortran代码,我试图通过一个Cython扩展模块来包装它(已经为其他Fortran库完成了)。该代码在Fortran内部调用时可以工作,但是当通过Cython包装器访问时,它会强制程序停止,退出代码为-1073741819 (0xC0000005)
或-1
,并且没有其他消息。更重要的是:这个崩溃发生在随机时间:有时Fortran代码会设法返回,但Python脚本仍然会在返回后崩溃。
我不确定问题在哪里,因为Fortran子程序有一个简单的签名签名,这应该使接口相对简单。下面是更多的细节(尽我所能删减)。
想法非常感谢!
Cython设置相关文件:
mylib.lib
:原始Fortran库代码wrapper.h
: Cython 头文件cython_module.pyx
: Cython codewrapper.f90
:用于将Fortran库绑定到C代码的包装代码wrapper.o
:使用ifort编译器编译Fortran包装器获得。命令:ifort wrapper.f90 /c /o wrapper.o /O3
setup.py
:编译设置这是编译设置文件。正如我之前提到的,我已经为其他Fortran-Cython接口使用了几乎相同的脚本,所以应该没问题,但是……
from distutils.core import setup
from Cython.Distutils import build_ext
from Cython.Distutils import Extension # Adds functionality to the standard distutils.extension.Extension
import Cython.Compiler.Options
from numpy import get_include
module_name = 'cython_module'
cython_interface = 'cython_module.pyx'
link_args = ['wrapper.o', 'mylib.lib']
fortran_libs = [ # Additional library directories for .lib files
r'C:Program Files (x86)IntelSWToolscompilers_and_libraries_2020.2.254windowscompilerlibintel64_win'
]
# Generate an additional HTML file with some feedback on optimization quality
Cython.Compiler.Options.annotate = True
ext_modules = [Extension(
module_name,
[cython_interface],
# I need this for using Complex numbers in other library functions not shown here
define_macros=[("CYTHON_CCOMPLEX", "0")],
extra_compile_args=['/O2'],
extra_link_args=link_args,
library_dirs=fortran_libs,
cython_directives={'language_level': "3"}
)]
setup(name='cython_module',
cmdclass={'build_ext': build_ext},
# Needed if building with NumPy.
include_dirs=[get_include()],
ext_modules=ext_modules)
Cython代码cython_module.pyx
目录:
import numpy as np
cdef extern from "<complex.h>": # You can ignore this
ctypedef struct _Dcomplex
cdef extern from "wrapper.h" nogil:
void py_indexh(double* a, double* b, long long* m, long long* n);
def indexh(double px, double py):
cdef long long[:] m = np.empty((1000,), dtype=np.int64, order='F')
cdef long long[:] n = np.empty((1000,), dtype=np.int64, order='F')
py_indexh(&px, &py, <long long*> m[0], <long long*> n[0])
return m, n
头文件(为了完整,非常简单):
#include <complex.h>
extern void py_indexh(double* a, double* b, long long* m, long long* n);
Fortran包装
我只展示了Fortran包装器,而不是整个库子程序体,因为它相当长且令人费解,并且当从Fortran本身调用时,它显然可以工作。
subroutine py_indexh(a, b, M, N) bind(c)
use iso_c_binding, only: c_float, c_double, c_int, c_int32_t, c_int64_t, c_double_complex, c_bool
implicit none
interface
subroutine indexh(a, b, M, N)
real(8), intent(in) :: a, b
integer(8), intent(out) :: M(1000), N(1000)
end subroutine indexh
end interface
real(c_double), intent(in) :: a, b
integer(C_INT64_T), intent(out) :: M(1000), N(1000)
print *, 'Entering indexh from Fortran...'
call indexh(a, b, M, N)
print *, 'Exitted indexh from Fortran...'
end subroutine py_indexh
issue/output
代码编译时没有重大问题。只显示了这个警告,我认为这与.lib
文件的编译方式有关。请让我知道我是否应该对此更加担心,它在我所做的其他Fortran的Cython包装中没有造成任何伤害:LINK : warning LNK4098: defaultlib 'LIBCMT' conflicts with use of other libs; use /NODEFAULTLIB:library
当导入cython模块并调用cython_module.indexh(3e-3, 4e-3)
时,它会无声地崩溃,退出代码为-1
或-1073741819 (0xC0000005)
。但是,在崩溃之前,Fortran子例程成功进入,因为'Entering indexh from Fortran...'
在控制台上打印出来了。有时,甚至会显示第二次打印'Exitted indexh from Fortran...'
,这应该表明子例程已经完成。但是,无论显示一个还是两个打印结果,程序都会在函数调用之后崩溃。
只是澄清一下,在indexh(...)
的主体中没有奇怪的多线程业务或任何不寻常的东西,只有许多嵌套的循环动作。
<long long*> m[0], <long long*> n[0]
将m
和n
的第一个元素(即整数)强制转换为long long*
。
你想要的
&m[0], &n[0]
获取第一个元素的地址。
强制类型转换通常只是在编译器告诉你一个错误时关闭它的一种方式。