使用 Lua 语言及其脚本实现 Fortran 程序的热重载

fortran-lua53🔥仓库 (许可证: ISC,类似BSD-2): https://github.com/interkosmos/fortran-lua53

fortran-luaPhilipp 编写的开源项目,它是 Lua 5.3 的 Fortran 接口。

Lua

Lua 简介

Lua 由 C 语言编程,可以作为单独的 Lua 解释器或 C 语言函数库,是一名优秀小巧的函数式动态语言,其速度是动态语言中 Top 0 级别的,另外,Lua 语言是罕见的由发展中国家设计开发出的著名语言。

它的主要应用场景是:

  • 为编译型语言带来灵活性;
  • 为编译型语言代码可拓展性,这一点体现在热重载能力。

🔰 热重载: 一般情况下,当不具备任何反射特性的编译型语言一旦完成编译,实现部署,中途如果发现 Bug ,则需要重新修改源码,再进行编译部署,且需要中断之前正在运行的含 Bug 的程序。
但是,通过 Lua 脚本,比如 Fortran ,可以借助 Lua 只实现 Lua 脚本的内容,就能一定程度改变程序的后续运行过程,可以一定程度避免中断运行。
这点与配置文件似乎有着异曲同工之妙,但配置文件主要负责程序数据的配置,而 Lua 脚本则更体现出从函数(方法)上改变程序的运行业务,且更灵活,数据配置亦可。
所以,Lua 在游戏开发行业用的较多,可以为上线的游戏快速推送 Lua 补丁,性能又好,体积又小。 在科学计算中,如计算流体力学,我们可以通过自定义lua脚本实现网格、粒子的初始化,从内存传递给核心计算程序。

演示: 使用 Lua 脚本改变程序的运行效果

我们演示一下在代码编写过程中写错了 Lua 业务,然后在不修改 Fortran 代码、不重新编译代码、不中断程序运行的情况下,实现 Lua 业务修改和正确运行:

cd workspace  # 切换到你常用的工作区间
fpm new --app lua-demo  # 创建fpm项目
cd lua-demo && code .   # 切换到`lua-demo`文件夹,并使用vs code打开它

我们创建了一个lua-demo工程,使用vs code打开了它,我们可以在fpm工程的fpm.toml文件中添加以下语句,以使用fortran-lua53

[dependencies]
fortran-lua53 = { git = "https://github.com/interkosmos/fortran-lua53.git" }

错误业务

上图,我们“不小心”在 Lua 脚本中写了一个错误的times函数(粗心的程序员写成了加法函数),可是程序已经运行了,幸亏这个 Bug 不会导致程序崩溃,我们得赶紧修改它,并且将这个补丁发送给用户。

 5 * 5 =           10
 按回车继续运行..

正确业务

我们在程序运行的间隙,修改了times函数的业务,并且推送给用户运行环境,此后程序业务得到正常合理运行。

 5 * 5 =           25
 按回车继续运行..

我们能看到,Lua 脚本的强大之处,尤其是对强编译型语言的特殊需求,它使得程序变得更灵活,相当于用户业务程序实时自带了一个实时高效的 Lua 解释器。

大多数情况下,这个特性常规用户是不太需要的,但确实为编程提供了一种特色强烈的可能性,跨语言交互和热重载。

代码

program main
    
    use, intrinsic :: iso_c_binding, only: c_ptr
    use lua
    implicit none
    !> C 指针, 可以指向任何类型的 C 对象
    type(c_ptr) :: l
    integer :: rc
    
    do
        !> 启动 Lua 虚拟机
        l = lual_newstate()
        call lual_openlibs(l)
        rc = lual_dofile(l, "times.lua")
        rc = lua_getglobal(l, "times")
        
        !> 推送参数值
        call lua_pushinteger(l, 5)
        call lua_pushinteger(l, 5)
        rc = lua_pcall(l, 2, 1, 0) ! 调用times函数,推送2个参数,返回1个结果
        
        !> 获取返回值
        rc = lua_tointeger(l, -1) ! -index: 第几个返回值
        call lua_pop(l, 1)        ! 弹出返回值及其数量
        
        print *, "5 * 5 = ", rc
        print *, "按回车继续运行.."
        read (*, *)
        
    end do
    
end program main
-- 整数乘法
function times(a, b)  
    return a + b
end