在 Fortran 90 中将 functions/subroutine 声明与定义分开的简洁方法

Clean way to separate functions/subroutine declaration from definition in Fortran 90

我正在编写一个包含很多模块的大型 Fortran 90 代码。困扰我的是,当我修改模块内函数的内部代码(不更改其掩码)时,我的 Makefile(其依赖项基于 "use")重新编译 "use" 修改模块的每个文件, 递归地.

但是在不触及函数内部代码的情况下修改函数的内部代码时input/output,重新编译修改后的其他文件是没有用的,不是吗?

所以我想将函数声明与其定义分开,就像 C 或 C++ 中的 .h 文件一样。这样做的干净方法是什么?我是否必须使用 Fortran include/preprocessor #include,或者是否有 "module/use" 的方法?

我试过类似的东西,但是好像很扯淡...

main.f90

program prog

  use foomod_header

  integer :: i

  bar=0
  i=42
  call foosub(i)

end program prog

foomod_header.f90

module foomod_header

  integer :: bar

  interface 
    subroutine foosub(i)
      integer :: i
    end subroutine
  end interface

end module foomod_header

foomod.f90

module foomod

  use foomod_header

  contains

  subroutine foosub(i)
    integer ::i

    print *,i+bar

  end subroutine foosub

end module foomod

如果子模块不是一个选项(并且它们是理想的选择),那么您可以做的是使过程成为外部过程并在模块中为该过程提供接口。例如:

! Program.f90
PROGRAM p
  USE Interfaces
  IMPLICIT NONE
  ...
  CALL SomeProcedure(xyz)
END PROGRAM p

! Interfaces.f90
MODULE Interfaces
  IMPLICIT NONE
  INTERFACE
    SUBROUTINE SomeProcedure(some_arg)
      USE SomeOtherModule
      IMPLICIT NONE
      TYPE(SomeType) :: some_arg
    END SUBROUTINE SomeProcedure
  END INTERFACE
END MODULE Interfaces

! SomeProcedure.f90
SUBROUTINE SomeProcedure(some_arg)
  USE SomeOtherModule
  IMPLICIT NONE
  TYPE(SomeType) :: some_arg
  ...
END SUBROUTINE SomeProcedure

一些重要说明:

  • 一个范围内可访问的过程只能有一个接口定义。在子程序内部,由子程序定义的过程的接口也被认为是已定义的——因此在子程序内部,您不能允许访问由子程序定义的过程的接口块。就示例而言,这意味着在 SomeProcedure 外部过程中不能有没有 only 子句的 USE Interfaces 语句。

  • 如果你确实更改了SomeProcedure.f90中程序的参数或类似内容,你最好确保你更改了模块中相应的接口块!

  • 如果能用F2003,IMPORT语句就可以轻松搞定。否则,您可能需要额外的模块(例如示例中的 SomeOtherModule)才能在接口模块和外部过程之间共享类型定义等。

  • 如果您有与过程相关的私有实体或组件,则 Fortran 规则实体和组件可访问性可能会阻止您使用此方法。

  • 通常,某种完整的程序分析是在高度优化的情况下完成的。该分析通常比实际解析代码慢得多 - 在这些条件下,以这种方式拆分过程实际上可能不会显着缩短构建时间。

也许最干净的解决方案是更改构建系统。

USE 语句引入的真正依赖不是 source-code 文件,而是生成的 .mod 文件,它充当一种“二进制头文件”。 IE。 makefile 通常包含类似

的内容
MyProgram.o: MyModule.f90

它们真正应该包含的是

MyProgram.o: MyModule.mod
MyModule.mod: MyModule.f90

创建 .mod 文件的方式可以确保 file-system 时间戳不变,如果界面实际上没有改变的话。

可悲的是,compiler-support很尴尬。大多数编译器无论如何都会覆盖 .mod 文件,因此构建过程必须同时检测到 .mod 文件没有改变,例如通过在内容不变的情况下恢复旧的修改时间,但同时需要避免不必要地重新编译源文件,这需要更新.mod文件的修改时间。

此外,一些编译器(Intel,*咳嗽*)将二进制 time-stamp 添加到 .mod 文件的 contents 中,这需要手动排除在比较之外,并在不同版本中更改了二进制位置。这在支持多个编译器时会增加工作量。