我正在开发一个包含大量模块的大型Fortran 90代码。困扰我的是,当我修改模块内部函数的内部代码(不更改其掩码)时,我的Makefile(其依赖项基于“use”)重新编译“使用”该修改模块的每个文件,并递归。
但是当修改函数的内部代码而不触及其输入/输出时,重新编译除修改后的文件之外的其他文件是没用的,不是吗?
所以我想将函数声明与它们的定义分开,就像在C或C ++中使用.h文件一样。干净的方法是什么?我是否必须使用Fortran包含/预处理器#include
,或者是否有“模块/使用”方式来执行此操作?
我尝试过这样的事情,但这似乎是无稽之谈......
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
答案 0 :(得分:2)
如果子模块不是一个选项(并且它们是理想的选项),那么您可以做的是使该过程成为外部过程并为模块中的该过程提供接口。例如:
! 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
一些重要的注释:
对于在作用域中可访问的过程,必须只有一个接口定义。在子程序内部,子程序定义的过程的接口也被认为是定义的 - 因此在子程序中,您不能允许接口块用于子程序定义的过程。就示例而言,这意味着在USE Interfaces
外部过程中没有唯一子句时,您不能拥有SomeProcedure
语句。
如果您确实更改了SomeProcedure.f90中的过程或类似过程,您最好确保更改模块内的相应接口块!
如果您可以使用F2003,IMPORT语句可以让生活更轻松。否则,您可能必须具有其他模块(例如示例中的SomeOtherModule
)以在Interfaces模块和外部过程之间共享类型定义等。
如果您拥有与该过程相关的私有实体或组件,那么Fortran的规则实体和组件可访问性可能会阻止您使用此方法。
通常,某种程序的整体程序分析是在高水平的优化下完成的。该分析通常比实际解析代码慢得多 - 以这种方式拆分过程实际上可能不会在这些条件下显着缩短构建时间。
答案 1 :(得分:0)
也许最干净的解决方案是更改构建系统。
USE
语句引入的实际依赖关系不是源代码文件,而是生成的.mod
文件,它充当一种“二进制头文件”。即Makefile通常包含类似
MyProgram.o: MyModule.f90
它们真正应该包含的是
MyProgram.o: MyModule.mod
MyModule.mod: MyModule.f90
.mod
文件的创建以某种方式完成,如果接口没有实际更改,则可以确保文件系统时间戳不变。
可悲的是,编译器支持很尴尬。大多数编译器无论如何都会覆盖.mod
文件,因此构建过程必须同时检测.mod
文件没有更改,例如。通过在内容不变的情况下恢复旧的修改时间,但同时又需要避免不必要地重新编译源文件,这需要更新.mod
文件的修改时间。
此外,某些编译器(Intel,* cough *)向.mod
文件的 contents 添加了二进制时间戳,需要手动将它们从比较中排除,并具有跨发行版更改了二进制位置。在支持多个编译器时,这会增加工作量。