在 Fortran 中使用子模块进行通用分配
Using SubModules for Generic Assignments in Fortran
如果我们有三个不同派生类型的文件,
MyTypeMod.f90:
MODULE MyTypeMod
TYPE, ABSTRACT :: MyType
INTEGER :: Num
END TYPE MyType
CONTAINS
END MODULE MyTypeMod
MyType1Mod.f90
MODULE MyType1Mod
USE MyTypeMod, ONLY : MyType
USE MyType2Mod, ONLY : MyType2
IMPLICIT NONE
TYPE, EXTENDS(MyType) :: MyType1
CONTAINS
PROCEDURE :: Type1EqualsType2
GENERIC :: ASSIGNMENT(=) => Type1EqualsType2
END TYPE MyType1
CONTAINS
SUBROUTINE Type1EqualsType2(Type1, Type2)
TYPE(MyType1), INTENT(OUT) :: Type1
TYPE(MyType2), INTENT(IN) :: Type2
Type1%Num = Type2%Num
END SUBROUTINE Type1EqualsType2
END MODULE MyType1Mod
MyType2Mod.f90
MODULE MyType1Mod
USE MyTypeMod, ONLY : MyType
USE MyType1Mod, ONLY : MyType1
IMPLICIT NONE
TYPE, EXTENDS(MyType) :: MyType2
CONTAINS
PROCEDURE :: Type2EqualsType1
GENERIC :: ASSIGNMENT(=) => Type2EqualsType1
END TYPE MyType2
CONTAINS
SUBROUTINE Type2EqualsType1(Type2, Type1)
TYPE(MyType2), INTENT(OUT) :: Type2
TYPE(MyType1), INTENT(IN) :: Type1
Type2%Num = Type1%Num
END SUBROUTINE Type2EqualsType1
END MODULE MyType2Mod
在这里,在这种情况下,由于模块文件相互依赖,我无法编译程序。我可以使用 SubModules 来解决问题吗?
很遗憾,不能,您不能使用子模块做您想做的事。这是因为函数 Type1EqualsType2
和 Type2EqualsType1
在它们的函数接口中都需要 MyType1
和 MyType2
。即使你使用子模块,两个函数都必须在各自的模块中有接口,因此循环依赖仍然存在。
但是,有几个可能的解决方法:
Select类型
您可以将两个函数的 intent(in)
参数设为 class(MyType)
,并且只使用 select type
语句进行类型解析。这将允许您将函数定义移动到子模块并解决循环依赖,但也意味着您必须处理将扩展 MyType
的不同类型传递给函数的情况。此外,select type
可能会有点慢,具体取决于您的用例。
此代码类似于:
MODULE MyTypeMod
IMPLICIT NONE
TYPE, ABSTRACT :: MyType
INTEGER :: Num
END TYPE MyType
END MODULE MyTypeMod
MODULE MyType1Mod
USE MyTypeMod, ONLY : MyType
IMPLICIT NONE
TYPE, EXTENDS(MyType) :: MyType1
CONTAINS
PROCEDURE :: Type1EqualsType2
GENERIC :: ASSIGNMENT(=) => Type1EqualsType2
END TYPE
interface
module SUBROUTINE Type1EqualsType2(this, input)
TYPE(MyType1), INTENT(OUT) :: this
class(MyType), INTENT(IN) :: input
END SUBROUTINE
end interface
END MODULE
MODULE MyType2Mod
USE MyTypeMod, ONLY : MyType
IMPLICIT NONE
TYPE, EXTENDS(MyType) :: MyType2
CONTAINS
PROCEDURE :: Type2EqualsType1
GENERIC :: ASSIGNMENT(=) => Type2EqualsType1
END TYPE
interface
module SUBROUTINE Type2EqualsType1(this, input)
TYPE(MyType2), INTENT(OUT) :: this
class(MyType), INTENT(IN) :: input
END SUBROUTINE
end interface
END MODULE
submodule (MyType1Mod) MyType1Submod
use MyType2Mod, only : MyType2
implicit none
contains
module procedure MyType1EqualsMyType2
select type(input); type is(MyType1)
this%Num = input%Num
type is(MyType2)
this%Num = input%Num
class default
! Some kind of error handling goes here.
end select
end procedure
end submodule
submodule (MyType2Mod) MyType2Submod
use MyType1Mod, only : MyType1
implicit none
contains
module procedure MyType2EqualsMyType1
select type(input); type is(MyType1)
this%Num = input%Num
type is(MyType2)
this%Num = input%Num
class default
! Some kind of error handling goes here.
end select
end procedure
end submodule
通用过程
您可以将类型绑定的 assignment(=)
定义替换为通用的 assignment(=)
定义。这避免了运行时多态性,但意味着您必须在新模块中定义分配。
这看起来像:
MODULE MyTypeMod
IMPLICIT NONE
TYPE, ABSTRACT :: MyType
INTEGER :: Num
END TYPE MyType
END MODULE MyTypeMod
MODULE MyType1Mod
USE MyTypeMod, ONLY : MyType
IMPLICIT NONE
TYPE, EXTENDS(MyType) :: MyType1
END TYPE
END MODULE
MODULE MyType2Mod
USE MyTypeMod, ONLY : MyType
IMPLICIT NONE
TYPE, EXTENDS(MyType) :: MyType2
END TYPE
END MODULE
module MyEqualsMod
use MyType1Mod : only MyType1
use MyType2Mod : only MyType2
implicit none
interface assignment(=)
module procedure MyType1EqualsMyType2
module procedure MyType2EqualsMyType1
end interface
contains
subroutine MyType1EqualsMyType2(this,input)
type(MyType1), intent(out) :: this
type(MyType2), intent(in) :: input
this%Num = input%Num
end subroutine
subroutine MyType2EqualsMyType1(this,input)
type(MyType2), intent(out) :: this
type(MyType1), intent(in) :: input
this%Num = input%Num
end subroutine
end module
如果我们有三个不同派生类型的文件,
MyTypeMod.f90:
MODULE MyTypeMod
TYPE, ABSTRACT :: MyType
INTEGER :: Num
END TYPE MyType
CONTAINS
END MODULE MyTypeMod
MyType1Mod.f90
MODULE MyType1Mod
USE MyTypeMod, ONLY : MyType
USE MyType2Mod, ONLY : MyType2
IMPLICIT NONE
TYPE, EXTENDS(MyType) :: MyType1
CONTAINS
PROCEDURE :: Type1EqualsType2
GENERIC :: ASSIGNMENT(=) => Type1EqualsType2
END TYPE MyType1
CONTAINS
SUBROUTINE Type1EqualsType2(Type1, Type2)
TYPE(MyType1), INTENT(OUT) :: Type1
TYPE(MyType2), INTENT(IN) :: Type2
Type1%Num = Type2%Num
END SUBROUTINE Type1EqualsType2
END MODULE MyType1Mod
MyType2Mod.f90
MODULE MyType1Mod
USE MyTypeMod, ONLY : MyType
USE MyType1Mod, ONLY : MyType1
IMPLICIT NONE
TYPE, EXTENDS(MyType) :: MyType2
CONTAINS
PROCEDURE :: Type2EqualsType1
GENERIC :: ASSIGNMENT(=) => Type2EqualsType1
END TYPE MyType2
CONTAINS
SUBROUTINE Type2EqualsType1(Type2, Type1)
TYPE(MyType2), INTENT(OUT) :: Type2
TYPE(MyType1), INTENT(IN) :: Type1
Type2%Num = Type1%Num
END SUBROUTINE Type2EqualsType1
END MODULE MyType2Mod
在这里,在这种情况下,由于模块文件相互依赖,我无法编译程序。我可以使用 SubModules 来解决问题吗?
很遗憾,不能,您不能使用子模块做您想做的事。这是因为函数 Type1EqualsType2
和 Type2EqualsType1
在它们的函数接口中都需要 MyType1
和 MyType2
。即使你使用子模块,两个函数都必须在各自的模块中有接口,因此循环依赖仍然存在。
但是,有几个可能的解决方法:
Select类型
您可以将两个函数的 intent(in)
参数设为 class(MyType)
,并且只使用 select type
语句进行类型解析。这将允许您将函数定义移动到子模块并解决循环依赖,但也意味着您必须处理将扩展 MyType
的不同类型传递给函数的情况。此外,select type
可能会有点慢,具体取决于您的用例。
此代码类似于:
MODULE MyTypeMod
IMPLICIT NONE
TYPE, ABSTRACT :: MyType
INTEGER :: Num
END TYPE MyType
END MODULE MyTypeMod
MODULE MyType1Mod
USE MyTypeMod, ONLY : MyType
IMPLICIT NONE
TYPE, EXTENDS(MyType) :: MyType1
CONTAINS
PROCEDURE :: Type1EqualsType2
GENERIC :: ASSIGNMENT(=) => Type1EqualsType2
END TYPE
interface
module SUBROUTINE Type1EqualsType2(this, input)
TYPE(MyType1), INTENT(OUT) :: this
class(MyType), INTENT(IN) :: input
END SUBROUTINE
end interface
END MODULE
MODULE MyType2Mod
USE MyTypeMod, ONLY : MyType
IMPLICIT NONE
TYPE, EXTENDS(MyType) :: MyType2
CONTAINS
PROCEDURE :: Type2EqualsType1
GENERIC :: ASSIGNMENT(=) => Type2EqualsType1
END TYPE
interface
module SUBROUTINE Type2EqualsType1(this, input)
TYPE(MyType2), INTENT(OUT) :: this
class(MyType), INTENT(IN) :: input
END SUBROUTINE
end interface
END MODULE
submodule (MyType1Mod) MyType1Submod
use MyType2Mod, only : MyType2
implicit none
contains
module procedure MyType1EqualsMyType2
select type(input); type is(MyType1)
this%Num = input%Num
type is(MyType2)
this%Num = input%Num
class default
! Some kind of error handling goes here.
end select
end procedure
end submodule
submodule (MyType2Mod) MyType2Submod
use MyType1Mod, only : MyType1
implicit none
contains
module procedure MyType2EqualsMyType1
select type(input); type is(MyType1)
this%Num = input%Num
type is(MyType2)
this%Num = input%Num
class default
! Some kind of error handling goes here.
end select
end procedure
end submodule
通用过程
您可以将类型绑定的 assignment(=)
定义替换为通用的 assignment(=)
定义。这避免了运行时多态性,但意味着您必须在新模块中定义分配。
这看起来像:
MODULE MyTypeMod
IMPLICIT NONE
TYPE, ABSTRACT :: MyType
INTEGER :: Num
END TYPE MyType
END MODULE MyTypeMod
MODULE MyType1Mod
USE MyTypeMod, ONLY : MyType
IMPLICIT NONE
TYPE, EXTENDS(MyType) :: MyType1
END TYPE
END MODULE
MODULE MyType2Mod
USE MyTypeMod, ONLY : MyType
IMPLICIT NONE
TYPE, EXTENDS(MyType) :: MyType2
END TYPE
END MODULE
module MyEqualsMod
use MyType1Mod : only MyType1
use MyType2Mod : only MyType2
implicit none
interface assignment(=)
module procedure MyType1EqualsMyType2
module procedure MyType2EqualsMyType1
end interface
contains
subroutine MyType1EqualsMyType2(this,input)
type(MyType1), intent(out) :: this
type(MyType2), intent(in) :: input
this%Num = input%Num
end subroutine
subroutine MyType2EqualsMyType1(this,input)
type(MyType2), intent(out) :: this
type(MyType1), intent(in) :: input
this%Num = input%Num
end subroutine
end module