Ubuntu下Fortran与其他语言的协同工作指南
一、总体思路与工具
- 核心机制:优先使用Fortran 2003 的 ISO_C_Binding 定义与 C ABI 兼容的接口,配合 bind(C, name=“…”) 固定导出符号名,避免名称修饰带来的不匹配。
- 编译器与构建:安装 gfortran、gcc/g++;生成位置无关代码用 -fPIC,构建共享库用 -shared;链接阶段注意库的顺序与链接 Fortran 运行时(必要时显式加 -lgfortran)。运行时动态库路径可用 LD_LIBRARY_PATH 指定。
二、与C C++的协同
- 与C语言
- 接口与类型:在 Fortran 中使用 use iso_c_binding,以 bind© 导出例程,参数用 c_int/c_double 等 C 兼容类型;C 端按指针接收/传递数据(Fortran 默认传地址)。
- 名称约定:现代方式用 bind(C, name=“xxx”) 精确控制导出名;旧式或特定编译器可能使用下划线后缀(如
sub_),不建议依赖。
- 编译链接:gfortran 编译 Fortran 与链接最终可执行文件;C 端用 gcc;生成共享库时两者均可用 -fPIC -shared。示例:
- Fortran:
subroutine add(a, b, c) bind(C, name="add") …
- C:
void add(double *a, double *b, double *c); 调用时传地址。
- 链接:
gfortran main.o add.o -o app 或生成共享库后 gcc main.o -L. -ladd -lgfortran -o app。
- 与C++
- 在 C++ 中调用 Fortran:用 extern “C” 包裹 Fortran 的 C 接口声明,避免 C++ 名称改编;参数仍按 C ABI 以指针传递。
- 在 Fortran 中调用 C++:将 C++ 函数以 extern “C” 形式提供(或写一层薄 C 包装),再在 Fortran 中通过 bind© 调用。
三、与Python的协同
- f2py(NumPy 生态):将 Fortran 源码直接包装为 Python 模块,适合数值计算与数组密集型任务,安装 numpy 后使用
f2py -c file.f90 -m mod 生成扩展模块,Python 中 import mod 直接调用。
- ctypes:将 Fortran 编译为 共享库 .so,在 Python 中用 ctypes.CDLL 加载并按 C ABI 传参(注意连续内存布局、传址/传值)。
- Cython/C 包装:当需更精细控制或封装复杂接口时,可采用 Python → Cython → C → Fortran 的链路:Fortran 用 bind© 导出;C 做薄封装;Cython 生成 Python 扩展模块,便于打包发布。
四、与Java和.NET的协同
- Java:将 Fortran 编译为 .so,Java 通过 JNA 调用(无需 JNI 样板),按 C ABI 定义接口与参数类型,注意内存与字符串编码处理。
- .NET(C#):将 Fortran 编译为 .so,C# 用 DllImport 导入,指定 CallingConvention.Cdecl(或匹配实际约定),按 C ABI 传参;在 Ubuntu 22.04/.NET 8 环境下实测可行。
五、常见坑与最佳实践
- 参数传递与可变性:Fortran 子程序参数默认传地址;对应 C/C++ 端需使用指针。值语义可在 Fortran 端用
value 属性(ISO_C_Binding)或在 C 端复制值。
- 数据类型匹配:优先用 iso_c_binding 中的 c_int/c_long/c_double 等类型,避免隐式假设(如默认整数/实数精度)。
- 数组布局:Fortran 默认列主序(column-major),C/Python/NumPy 多为行主序(row-major);跨语言传递多维数组时需转置或按内存线性布局一致处理。
- 名称修饰与链接顺序:现代项目统一用 bind(C, name=“…”) 控制导出名;链接时按依赖顺序放置对象与库,必要时显式加 -lgfortran。
- 构建与运行时:生成共享库加 -fPIC -shared;运行前设置 LD_LIBRARY_PATH 或使用 rpath 将库搜索路径嵌入可执行文件。