如何为含非C互操作派生类型的Fortran程序构建Python接口?
为非C互操作Fortran派生类型构建Python接口(Cython方案)
核心思路是用C互操作的不透明句柄封装原有非互操作类型,让Python/Cython只处理句柄,所有类型内部操作都在Fortran层完成,完全保留原有代码的模块化和多实例支持。
步骤1:Fortran层封装非互操作类型
不需要修改原有SomeType的定义和内部逻辑,只需要新增一套C互操作的接口子程序,通过句柄(指针包装)间接操作原有类型。
1.1 定义C互操作的句柄类型与封装接口
! 新增对外接口模块 module SomeType_C_Interface use iso_c_binding use SomeType_Module ! 导入原有定义SomeType的模块 implicit none ! C互操作的句柄,封装SomeType实例的指针 type, bind(c) :: SomeType_Handle type(c_ptr) :: ptr = c_null_ptr ! 存储SomeType实例的内存地址 end type SomeType_Handle contains ! 创建SomeType实例并返回句柄 subroutine create_SomeType(handle) bind(c) type(SomeType_Handle), intent(out) :: handle type(SomeType), pointer :: instance allocate(instance) handle%ptr = c_loc(instance) ! Fortran指针转C兼容指针 end subroutine create_SomeType ! 封装原有init方法 subroutine init_SomeType(handle) bind(c) type(SomeType_Handle), intent(inout) :: handle type(SomeType), pointer :: instance call c_f_pointer(handle%ptr, instance) ! C指针转回Fortran指针 call instance%init() ! 调用原有类型绑定的初始化逻辑 end subroutine init_SomeType ! 封装do_some_stuff方法 subroutine do_some_stuff_SomeType(handle) bind(c) type(SomeType_Handle), intent(inout) :: handle type(SomeType), pointer :: instance call c_f_pointer(handle%ptr, instance) call instance%do_some_stuff() end subroutine do_some_stuff_SomeType ! 封装带参数的方法(参数需为C互操作类型) subroutine do_stuff_with_args_SomeType(handle, arg1, arg2) bind(c) type(SomeType_Handle), intent(inout) :: handle real(c_double), intent(in) :: arg1, arg2 ! 示例参数为C兼容的double type(SomeType), pointer :: instance call c_f_pointer(handle%ptr, instance) call instance%do_stuff_with_args(arg1, arg2) end subroutine do_stuff_with_args_SomeType ! 销毁实例释放内存 subroutine finalize_SomeType(handle) bind(c) type(SomeType_Handle), intent(inout) :: handle type(SomeType), pointer :: instance if (c_associated(handle%ptr)) then call c_f_pointer(handle%ptr, instance) call instance%finalize() deallocate(instance) handle%ptr = c_null_ptr end if end subroutine finalize_SomeType end module SomeType_C_Interface
步骤2:Cython层封装接口
通过Cython将Fortran的C互操作接口包装成Python类,让Python端调用流程与Fortran一致。
2.1 Cython代码(some_type.pyx)
cdef extern from "some_type_c_interface.h": # 对应Fortran的SomeType_Handle结构体 ctypedef struct SomeType_Handle: void* ptr # 声明Fortran导出的C接口函数 void create_SomeType(SomeType_Handle* handle) void init_SomeType(SomeType_Handle* handle) void do_some_stuff_SomeType(SomeType_Handle* handle) void do_stuff_with_args_SomeType(SomeType_Handle* handle, double arg1, double arg2) void finalize_SomeType(SomeType_Handle* handle) cdef class SomeType: cdef SomeType_Handle handle def __cinit__(self): # 创建Fortran实例 create_SomeType(&self.handle) def init(self): # 调用初始化方法 init_SomeType(&self.handle) def do_some_stuff(self): do_some_stuff_SomeType(&self.handle) def do_stuff_with_args(self, arg1, arg2): # 传递C兼容参数 do_stuff_with_args_SomeType(&self.handle, arg1, arg2) def __del__(self): # 自动销毁实例,避免内存泄漏 finalize_SomeType(&self.handle)
步骤3:编译与使用
3.1 编译Fortran为共享库
以gfortran为例:
# 编译Fortran代码为目标文件 gfortran -c -fPIC some_type_module.f90 some_type_c_interface.f90 -o some_type.o # 生成共享库 gfortran -shared some_type.o -o libsome_type.so
编译后会自动生成some_type_c_interface.h头文件(部分编译器需手动用gfortran -cpp -E预处理生成)。
3.2 编译Cython扩展
创建setup.py:
from setuptools import setup, Extension from Cython.Build import cythonize ext = Extension( name="some_type", sources=["some_type.pyx"], libraries=["some_type"], library_dirs=["."], include_dirs=["."] ) setup(ext_modules=cythonize(ext))
执行编译:
python setup.py build_ext --inplace
3.3 Python端调用
from some_type import SomeType # 调用流程与Fortran完全对齐 instance = SomeType() instance.init() instance.do_some_stuff() instance.do_stuff_with_args(1.5, 3.0) # 实例销毁时自动触发finalize,也可手动调用instance.__del__()
关键说明
- 原有
SomeType的非C互操作成员not_interoperable完全被隐藏在Fortran内部,Python无法访问,也不影响Fortran内部使用。 - 每个Python
SomeType实例对应独立的FortranSomeType实例,支持多实例,无全局变量问题。 c_loc和c_f_pointer是Fortran与C指针转换的核心,确保跨语言内存地址传递正确。
内容的提问来源于stack exchange,提问作者tmph
相关产品推荐
相关产品推荐

