在 Fortran 中使用子模块进行通用分配

Posted

技术标签:

【中文标题】在 Fortran 中使用子模块进行通用分配【英文标题】:Using SubModules for Generic Assignments in Fortran 【发布时间】:2021-12-07 20:31:20 【问题描述】:

如果我们有三个具有不同派生类型的不同文件,

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

【问题讨论】:

“我们如何创建子模块”是什么意思?你想达到什么目标?您希望哪个部分最终出现在子模块中?为什么? 【参考方案1】:

很遗憾,不,您不能使用子模块完全满足您的需求。这是因为 Type1EqualsType2Type2EqualsType1 两个函数都在其函数接口中需要 MyType1MyType2。即使你使用子模块,这两个函数都必须在各自的模块中有接口,所以循环依赖仍然存在。

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

选择类型

您可以将两个函数的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

【讨论】:

以上是关于在 Fortran 中使用子模块进行通用分配的主要内容,如果未能解决你的问题,请参考以下文章

使用 GCC 对模块内的 fortran 子例程进行外部命名

使用宏的 Fortran 替换子例程名称

php计算网段内所有IP,分配子网段

fortran 模块的子例程名称冲突

在 Android 中跨子模块使用强制通用 buildToolsVersion 有啥影响?

在Fortran中正确使用模块,子例程和函数