news 2026/4/23 13:11:40

Fortran 中动态加载动态库

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Fortran 中动态加载动态库

文章目录

    • 一、核心原理
    • 二、完整示例(Linux 平台)
      • 1. 动态库源码(`mylib.c`)
      • 2. C 封装层(`dlwrapper.c`)
      • 3. Fortran 主程序(`main.f90`)
      • 4. 编译与运行
    • 三、Windows 平台适配要点
    • 四、关键注意事项
    • 五、高级技巧:直接使用 libdl(无需 C 封装)
    • 六、适用场景

在 Fortran 中动态加载动态库(.so.dll)需借助C 互操作性(ISO_C_BINDING)调用操作系统原生 API(如dlopen/dlsymLoadLibrary/GetProcAddress)。Fortran 标准本身不提供动态加载接口,但可通过 C 绑定桥接实现。以下分步骤说明并提供完整示例。


一、核心原理

步骤说明
1. 编写动态库用 C 编写,使用extern "C"避免 C++ 名称修饰;Fortran 需通过 ISO_C_BINDING 声明接口
2. 动态加载通过 C 函数封装dlopen/LoadLibrary,Fortran 调用该封装函数
3. 获取符号通过 C 函数封装dlsym/GetProcAddress,返回函数指针或变量地址
4. 类型转换使用c_funptr/c_fptr在 C 函数指针与 Fortran 过程指针间转换

⚠️关键点:Fortran 无法直接操作void*,必须通过 C 封装层中转。


二、完整示例(Linux 平台)

1. 动态库源码(mylib.c

// mylib.c - 编译为 libmylib.so#include<stdio.h>// 全局变量doubleglobal_value=3.14159;// C 函数(无名称修饰)doublecompute(doublex,doubley){returnx*y+global_value;}// 辅助函数:返回变量地址(避免 Fortran 直接处理 void*)double*get_global_value_ptr(){return&global_value;}

编译动态库:

gcc -shared -fPIC -o libmylib.so mylib.c

2. C 封装层(dlwrapper.c

// dlwrapper.c - 封装 dlopen/dlsym#define_GNU_SOURCE#include<dlfcn.h>#include<stdint.h>// 返回句柄(转换为 intptr_t 供 Fortran 使用)intptr_topen_library(constchar*path){return(intptr_t)dlopen(path,RTLD_LAZY|RTLD_GLOBAL);}// 获取函数符号void*get_function(intptr_thandle,constchar*name){returndlsym((void*)handle,name);}// 获取变量地址void*get_variable(intptr_thandle,constchar*name){returndlsym((void*)handle,name);}// 关闭库voidclose_library(intptr_thandle){dlclose((void*)handle);}

编译为静态库供 Fortran 链接:

gcc -c -fPIC dlwrapper.c -o dlwrapper.o ar rcs libdlwrapper.a dlwrapper.o

3. Fortran 主程序(main.f90

program dynamic_load_example use, intrinsic :: iso_c_binding implicit none ! 接口声明 interface function open_library(path) bind(c, name='open_library') import :: c_char, c_intptr_t character(kind=c_char), intent(in) :: path(*) integer(c_intptr_t) :: open_library end function function get_function(handle, name) bind(c, name='get_function') import :: c_intptr_t, c_funptr integer(c_intptr_t), value :: handle character(kind=c_char), intent(in) :: name(*) type(c_funptr) :: get_function end function function get_variable(handle, name) bind(c, name='get_variable') import :: c_intptr_t, c_ptr integer(c_intptr_t), value :: handle character(kind=c_char), intent(in) :: name(*) type(c_ptr) :: get_variable end function subroutine close_library(handle) bind(c, name='close_library') import :: c_intptr_t integer(c_intptr_t), value :: handle end subroutine end interface ! 目标函数类型声明 abstract interface function compute_func(x, y) bind(c) import :: c_double real(c_double), value :: x, y real(c_double) :: compute_func end function end interface ! 变量 integer(c_intptr_t) :: lib_handle type(c_funptr) :: func_ptr type(c_ptr) :: var_ptr procedure(compute_func), pointer :: compute_fptr => null() real(c_double), pointer :: global_value_ptr => null() real(c_double) :: result ! 1. 加载库 lib_handle = open_library('libmylib.so' // c_null_char) if (lib_handle == 0) then print *, 'Error: Failed to load library' stop end if print *, 'Library loaded successfully' ! 2. 获取函数 func_ptr = get_function(lib_handle, 'compute' // c_null_char) if (.not. c_associated(func_ptr)) then print *, 'Error: Failed to get function "compute"' call close_library(lib_handle) stop end if call c_f_procpointer(func_ptr, compute_fptr) ! 3. 获取变量 var_ptr = get_variable(lib_handle, 'global_value' // c_null_char) if (.not. c_associated(var_ptr)) then print *, 'Error: Failed to get variable "global_value"' call close_library(lib_handle) stop end if call c_f_pointer(var_ptr, global_value_ptr) ! 4. 调用函数 & 访问变量 result = compute_fptr(2.0_c_double, 3.0_c_double) print *, 'compute(2.0, 3.0) = ', result print *, 'global_value = ', global_value_ptr ! 5. 修改变量(通过指针) global_value_ptr = 2.71828_c_double result = compute_fptr(1.0_c_double, 1.0_c_double) print *, 'After modification: compute(1.0,1.0) = ', result ! 6. 卸载库 call close_library(lib_handle) print *, 'Library unloaded' end program dynamic_load_example

4. 编译与运行

# 编译 Fortran 程序(链接 libdl)gfortran -o main main.f90 libdlwrapper.a -ldl# 运行(确保 libmylib.so 在 LD_LIBRARY_PATH 中)exportLD_LIBRARY_PATH=.:$LD_LIBRARY_PATH./main

输出示例

Library loaded successfully compute(2.0, 3.0) = 9.1415900000000003 global_value = 3.1415900000000001 After modification: compute(1.0,1.0) = 3.7182800000000001 Library unloaded

三、Windows 平台适配要点

  1. 动态库:编译为.dll,导出函数需加__declspec(dllexport)

    __declspec(dllexport)doublecompute(doublex,doubley){...}
  2. 封装层:替换为 Windows API

    #include<windows.h>HMODULEopen_library(constchar*path){returnLoadLibraryA(path);}FARPROCget_function(HMODULE h,constchar*name){returnGetProcAddress(h,name);}
  3. Fortran 接口:将intptr_t替换为type(c_ptr),其他逻辑相同

  4. 编译

    gfortran -o main.exe main.f90 dlwrapper.o -lkernel32

四、关键注意事项

问题解决方案
名称修饰C 库必须用extern "C"(C++)或 plain C;Fortran 调用时名称需与nm lib.so输出一致
调用约定Linux 默认cdecl;Windows 需显式指定bind(c)确保兼容
指针安全使用c_associated()检查指针有效性,避免解引用空指针
内存管理动态库卸载后,所有获取的函数/变量指针失效,需在close_library前释放
线程安全dlopen/LoadLibrary非线程安全,多线程环境需加锁

五、高级技巧:直接使用 libdl(无需 C 封装)

部分编译器(如 GCC 10+)支持直接绑定libdl函数:

interface function dlopen(filename, mode) bind(c, name='dlopen') import :: c_char, c_int, c_ptr character(kind=c_char), intent(in) :: filename(*) integer(c_int), value :: mode type(c_ptr) :: dlopen end function end interface

但需注意:

  • 需链接-ldl
  • RTLD_LAZY等常量需手动定义(如integer(c_int), parameter :: RTLD_LAZY = 1
  • 可移植性较差,推荐使用 C 封装层

六、适用场景

  • 插件化架构(运行时加载算法模块)
  • 避免硬编码依赖(如可选的 CUDA/MPI 支持)
  • 高性能计算中动态切换优化内核
  • 与 Python/C/C++ 生态集成(如通过 ctypes 加载 Fortran 库)

💡建议:生产环境推荐使用CMake + 预编译封装库管理依赖,避免手动处理符号和路径问题。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/17 5:34:58

远程育儿+量子开发:一位母亲的2026平衡术

在2026年的数字化浪潮中&#xff0c;公众号内容的热度不再依赖传统“热搜”&#xff0c;而是由用户需求数据驱动——点击率、停留时长和分享量成为算法推荐的核心指标。作为软件测试从业者&#xff0c;我们习惯于用精准的测试用例验证系统&#xff0c;但当育儿遇上远程工作&…

作者头像 李华
网站建设 2026/4/2 16:38:50

信创环境下,CKEDITOR导入Word文档时是否支持国产密码算法?

【西安码农の暴富日记】 嘿&#xff0c;各位十三朝古都的技术大拿们&#xff01;我是老张&#xff0c;西安回民街边撸肉夹馍边撸代码的Java程序猿。最近接了个"骚需求"——给CKEditor装个"Office全家桶导入微信内容一键吸星大法"插件&#xff0c;客户要求&…

作者头像 李华
网站建设 2026/4/18 15:48:39

强烈安利8个一键生成论文工具,继续教育学生必备!

强烈安利8个一键生成论文工具&#xff0c;继续教育学生必备&#xff01; AI 工具如何让论文写作更高效&#xff1f; 在当前的学术环境中&#xff0c;继续教育学生面临着日益繁重的论文写作任务。无论是课程作业还是学位论文&#xff0c;都需要大量的时间和精力去完成。而随着 A…

作者头像 李华
网站建设 2026/4/23 12:40:23

电力场景耐张线夹与绝缘子缺陷检测数据集VOC+YOLO格式2681张7类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2681 标注数量(xml文件个数)&#xff1a;2681 标注数量(txt文件个数)&#xff1a;2681 …

作者头像 李华
网站建设 2026/4/16 16:49:11

BXMya FOXBORO 2AX-ALM-AR 报警继电器模块

2AX-ALM-AR是美国福克斯波罗&#xff08;FOXBORO&#xff09;旗下2AX系列双通道绝对报警继电器模块&#xff0c;隶属于I/A Series分布式控制系统&#xff0c;又称报警跳闸模块&#xff0c;是工业过程控制系统中核心的安全报警辅助组件。该产品专为工业过程变量监测与异常报警设…

作者头像 李华