我有一个基本模块,定义了一些子程序(sub1
,sub2
,sub3
)。然后,我想在一系列子模块中覆盖这些子例程。
我知道如何使用单独的模块和延迟类型来做到这一点,但我决定尝试使用子模块。不幸的是,我不明白它们的用法。
这是我到目前为止:
BaseModule:
module BaseModule
implicit none
interface
subroutine sub1(idx)
implicit none
integer, intent(in) :: idx
end subroutine sub1
subroutine sub2(idx)
implicit none
integer, intent(in) :: idx
end subroutine sub2
subroutine sub3(idx)
implicit none
integer, intent(in) :: idx
end subroutine sub3
end interface
end module BaseModule
ChildModule1:
submodule (BaseModule) ChildModule1
implicit none
type :: Child1
contains
module procedure :: sub1
module procedure :: sub2
end type
contains
module subroutine sub1
print*, "Child 1 - execute 'sub1' - idx = ", idx
end subroutine sub1
module subroutine sub2
print*, "Child 1 - execute 'sub2' - idx = ", idx
end subroutine sub2
end submodule ChildModule1
ChildModule2:
submodule (BaseModule) ChildModule2
implicit none
type :: Child2
contains
module procedure :: sub1
module procedure :: sub2
module procedure :: sub3
end type
contains
module subroutine sub1
print*, "Child 2 - execute 'sub1' - idx = ", idx
end subroutine sub1
module subroutine sub2
print*, "Child 2 - execute 'sub2' - idx = ", idx
end subroutine sub2
module subroutine sub3
print*, "Child 2 - execute 'sub3' - idx = ", idx
end subroutine sub3
end submodule ChildModule2
测试:
program test
use ChildModule1
use Childmodule2
implicit none
integer :: idx
type(Child1) :: c1
type(Child2) :: c2
do idx = 1, 10
!! Child1 outputs
call c1%sub1(idx)
call c1%sub2(idx)
!! Child2 outputs
call c1%sub1(idx)
call c2%sub2(idx)
call c1%sub3(idx)
end do
end program test
我得到的子模块的想法是我不必再次声明所有的inouts,但是如果我想在更多的子模块中使用相同的子例程(例如sub1
)我会声明不同的类型呢?现在我得到编译错误:
Error: MODULE PROCEDURE at (1) must be in a generic module interface
我将尝试清除一些我认为你对Fortran中的子模块机制的误解。
然后,我想在一系列子模块中覆盖这些子例程。
您不会使用子模块覆盖过程,而是实现它们。
我得到子模块的想法是我不必再次声明所有的输入,
如果通过inouts表示过程的声明和签名(接口),那么是的,你不需要(但可以)在子模块中重复它们,但不是,这不是子模块的目的。
但是如果我想在更多的子模块中使用相同的子程序(例如
sub1
)我会声明不同的类型呢?
好吧,也许我必须简要解释一下子模块是什么以及它们不是什么。有关更多详细信息,请参阅@SteveLionel撰写的this good article,或Fortran wiki上的this entry,或compiler's reference(无论是什么),甚至是您最喜爱的Modern Fortran上的书。
正如我在another question中所说,子模块是为语言添加的一个特性,用于解决一个特定问题:接口和实现的分离。主要动机是当您需要更改模块中的实现细节时生成的编译级联。
子模块可以通过主机关联访问父模块上的实体,但父模块不知道此子模块的存在。当您在子模块Child1
中声明类型ChildModule1
时,它只能在此子模块本身内访问,但不能在父模块BaseModule
中访问。
此外,ChildModule1
不是一个模块,并且不能像你想要做的那样在主程序或任何其他程序单元中使用use
d。子模块的唯一作用是实现在其父模块中缺少实现的module procedures
。
总结:以模块化,合理的方式布置源文件和程序单元,并使用子模块,如果有必要使程序的实现独立于它们的声明(类似于c
头文件和源文件......但是请与一粒盐比较)。
编辑:
我发现你可能认为Fortran中的module
和submodule
与其他语言中的类和子类有关。他们没有!也许这是一个常见的误解,我不知道。
Fortran具有用户定义的类型。它们可以绑定方法,构造函数和析构函数,它们可以封装数据,可以扩展,可以动态调度,可以声明为抽象,可以推迟和覆盖成员。这相当于其他语言的类。您可以(并且这是一种很好的做法)将每种类型和相关的东西分成相应的模块。同样,如果需要,您可以为每种扩展类型创建一个模块。
但是,子模块再次与此无关。
为了总结这一点,我决定从我试图做的事情中给出一个有效的例子。再次感谢@Rodrigo Rodrigues澄清了什么是子模块。我不确定这种方法是否正确,但它对我有用。
任务
sub
定义类型sub
根据需要被覆盖基础模块
module BaseClass
implicit none
type, abstract :: Base ! <-- the base class with subroutine "sub"
contains
procedure(sub_interface), nopass, deferred :: sub
end type
interface
subroutine sub_interface(i) ! <-- the interface is defined here
implicit none
integer, intent(in) :: i
end subroutine sub_interface
end interface
end module BaseClass
儿童模块
module ChildClass
implicit none
type, extends(Base) :: Child ! <-- we extend the Base Class
contains
procedure, nopass :: sub
end type
interface
module subroutine sub(i) ! <-- the interface for the submodule (unfortunately we have to declare the entire thing again)
implicit none
integer, intent(in) :: i
end subroutine sub
end interface
end module ChildClass
子模块
submodule (ChildClass) ChildSub
contains
module procedure sub ! <-- we finally get to define the subroutine
print*, "The answer is :", i
end procedure
end submodule
该程序
program test
use ChildClass
implicit none
type(Child) :: c
integer :: i
do i=1, 10
call c%sub(i)
end do
end program test
我们现在可以有多个子类,它们都扩展了基类并相应地调用sub
。我个人不喜欢的是,我们必须两次声明sub
,一次在基类的iterface中,然后再在子类的接口中声明,以便使用子模块。这是代码重复,但也许这可以做得更好。