news 2026/5/4 13:19:45

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/5/5 1:08:10

计算机毕业设计springboot高校教室管理系统的设计与实现 基于SpringBoot的高校教学场地智能调度系统设计与实现 校园智慧教室资源管理与服务平台开发

计算机毕业设计springboot高校教室管理系统的设计与实现392lm110 &#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。 随着教育信息化进程的不断推进&#xff0c;高校教学资源的数字…

作者头像 李华
网站建设 2026/5/5 1:07:47

量子测试新规热度解析:聚焦从业者最关注的三大爆点

2026年1月&#xff0c;中国量子软件测试认证中心正式成立&#xff0c;为软件测试行业注入新动能。这一新规引发广泛讨论&#xff0c;公众号热度数据显示&#xff0c;从业者最关心“如何快速转型”“认证红利”及“实战避坑”三大方向。本文基于行业调研&#xff0c;深度解析热度…

作者头像 李华
网站建设 2026/5/5 2:52:26

互联网内容平台整合CKEDITOR后,如何解决Word文档转存的乱码问题?

ASP.NET WebForms CKEditor4 实现Word粘贴与文档导入功能方案 作为四川某软件公司的ASP.NET工程师&#xff0c;针对企业后台管理系统需求&#xff0c;结合Vue2/Vue3兼容性、CKEditor4集成及阿里云OSS存储要求&#xff0c;现提供完整技术方案与代码实现。 一、核心需求拆解与解…

作者头像 李华
网站建设 2026/5/5 2:53:20

基于SpringBoot的智慧公寓管理系统毕业设计源码

博主介绍&#xff1a;✌ 专注于Java,python,✌关注✌私信我✌具体的问题&#xff0c;我会尽力帮助你。一、研究目的本研究旨在设计并实现一个基于SpringBoot框架的智慧公寓管理系统&#xff0c;以提升公寓管理效率和服务质量。具体研究目的如下&#xff1a;提高公寓管理效率&am…

作者头像 李华
网站建设 2026/5/5 2:53:57

提升电子产品可靠性:深度解析散热器瞬态热管理

&#x1f393;作者简介&#xff1a;科技自媒体优质创作者 &#x1f310;个人主页&#xff1a;莱歌数字-CSDN博客 &#x1f48c;公众号&#xff1a;莱歌数字&#xff08;B站同名&#xff09; &#x1f4f1;个人微信&#xff1a;yanshanYH 211、985硕士&#xff0c;从业16年 从…

作者头像 李华
网站建设 2026/5/5 2:52:29

互联网大厂Java求职面试实录:核心技术栈与业务场景深度解析

文章内容&#xff1a; 在互联网大厂的Java求职面试中&#xff0c;技术细节和业务理解同等重要。本文通过一位严肃的面试官与一位搞笑的水货程序员谢飞机的三轮问答&#xff0c;带你深入了解面试中的技术考察与业务场景应用。 第一轮提问&#xff1a;Java核心与构建工具&#x…

作者头像 李华