You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Cython封装Fortran时派生类型与数组的C互操作性问题求解

Fortran C互操作性核心规则

派生类型互操作要求

  • 必须添加bind(C)修饰符
  • 所有成员类型必须是iso_c_binding模块定义的C兼容类型(如c_float对应C的float,c_int对应C的int)
  • 不能包含Fortran独有属性:比如allocatable、Fortran本地指针、parameter、可分配数组、假定长度数组
  • 内存布局和C结构体完全对齐,编译器不会私自调整成员顺序或添加填充位

数组互操作要求

  • 固定长度数组只要元素类型是C兼容的,就可以直接对应C的同长度数组,你代码里的dimension(1:2)属于固定长度数组,本身是支持互操作的
  • 动态数组(allocatable)不能直接互操作,需要用type(c_ptr)传递内存地址,或者封装为固定长度的包装类型

你遇到的两个错误的解决方法

错误1:派生类型geo重复定义

你同时通过use gfunc_module导入了原生的geo类型,又在封装层重新定义了同名的bind(C)版本geo,直接触发命名冲突。
有两种可选解决方案:

  • 测试场景下可以直接修改原生模块,给原生geo类型添加C互操作支持,不需要重复定义类型
  • 正式业务场景如果不能修改原有Fortran代码,就给封装层的C兼容类型改个名字,比如叫c_geo,在封装层写两种类型的转换逻辑即可

错误2:派生类型不具备C互操作性

报错有两个核心原因:

  1. 因为命名冲突,编译器实际优先识别了原生非C兼容的geo类型,而非你定义的bind(C)版本
  2. 你把c_gfunc子程序写在了geo类型的contains块内,这是Fortran的类型绑定子程序,默认会携带一个隐藏的this参数,不符合C调用约定,bind(C)的子程序必须放在模块的contains块内,不能定义为类型绑定方法

修正后的代码示例(适配无修改原生代码的业务场景)

如果你不能修改原有summation.f90,直接修改封装层即可:

pysummation.f90

module gfunc1_interface
use iso_c_binding
use gfunc_module
implicit none

! 单独定义C兼容的geo类型,不和原生类型重名
type, bind(c) :: c_geo
    real(c_float), dimension(1:2) :: coordinates 
    real(c_float) :: weight
end type

contains
! bind(C)的子程序放在模块的contains块下
subroutine c_gfunc(v1, v2, final_v) bind(c, name='c_gfunc')
    type(c_geo), intent(in) :: v1, v2
    type(c_geo), intent(out) :: final_v
    type(geo) :: f_v1, f_v2, f_res
    ! C兼容类型转原生Fortran类型
    f_v1%coordinates = v1%coordinates
    f_v1%weight = v1%weight
    f_v2%coordinates = v2%coordinates
    f_v2%weight = v2%weight
    ! 调用原生业务逻辑
    call gfunc(f_v1, f_v2, f_res)
    ! 结果转回C兼容类型
    final_v%coordinates = f_res%coordinates
    final_v%weight = f_res%weight
end subroutine

end module 

pysummation.h

// 补上缺失的分号,结构体名对应封装层的c_geo
struct c_geo {
    float coordinates[2];
    float weight;
};

extern void c_gfunc(struct c_geo *v1, struct c_geo *v2, struct c_geo *final_v);

pysummation.pyx

调整入参转换逻辑,适配Python侧的输入格式:

cdef extern from "pysummation.h":
    cdef struct c_geo: 
        float coordinates[2]
        float weight
    void c_gfunc(c_geo *v1, c_geo *v2, c_geo *final_v)

def f(v1, v2):
    # 输入格式约定:v1 = ((x1, y1), weight1),v2同理
    cdef:
        c_geo c_v1
        c_geo c_v2
        c_geo c_res
    # 填充入参
    c_v1.coordinates[0] = v1[0][0]
    c_v1.coordinates[1] = v1[0][1]
    c_v1.weight = v1[1]
    c_v2.coordinates[0] = v2[0][0]
    c_v2.coordinates[1] = v2[0][1]
    c_v2.weight = v2[1]
    # 调用接口
    c_gfunc(&c_v1, &c_v2, &c_res)
    # 转换为Python对象返回
    return ((c_res.coordinates[0], c_res.coordinates[1]), c_res.weight)

内容的提问来源于stack exchange,提问作者RUDH

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 12:15:03