在 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 来解决问题吗?

很遗憾,不能,您不能使用子模块做您想做的事。这是因为函数 Type1EqualsType2Type2EqualsType1 在它们的函数接口中都需要 MyType1MyType2。即使你使用子模块,两个函数都必须在各自的模块中有接口,因此循环依赖仍然存在。

但是,有几个可能的解决方法:

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