【问题标题】:Fortran choose derived type by procedure callFortran 通过过程调用选择派生类型
【发布时间】:2015-12-08 00:55:32
【问题描述】:

有没有办法使派生类型具有接口,以便为泛型派生类型调用特定的模块过程?我可能没有正确解释这一点,但这是我想做的简短示例代码:

  module test_mod
  implicit none

  private
  public :: type_AB ! This is what I'd like to do...
  public :: init,apply,delete

  interface type_AB ! This is what I'd like to do...
  module type type_AB
  end interface

  interface init;    module procedure init_A;      end interface
  interface init;    module procedure init_B;      end interface
  interface apply;   module procedure apply_A;     end interface
  interface apply;   module procedure apply_B;     end interface

  type type_A
    integer :: x
  end type

  type type_B
    integer,dimension(3) :: x
  end type

  contains

  subroutine init_A(AB,x)
    implicit none
    type(type_A),intent(inout) :: AB
    integer,intent(in) :: x
    AB%x = x
  end subroutine

  subroutine init_B(AB,x)
    implicit none
    type(type_B),intent(inout) :: AB
    integer,dimension(3),intent(in) :: x
    AB%x = 2
  end subroutine

  subroutine apply_A(AB,x)
    implicit none
    type(type_A),intent(inout) :: AB
    integer,intent(in) :: x
    AB%x = AB%x + x
  end subroutine

  subroutine apply_B(AB,x)
    implicit none
    type(type_B),intent(inout) :: AB
    integer,dimension(3),intent(in) :: x
    AB%x = AB%x + x
  end subroutine

  end module

在使用 test_mod 时,我可以简单地使用 type_AB 而不是指定 type_A 或 type_B。我知道我可以只指定两个对象,但是除了等级之外它们基本上是相同的,所以有一种组合/多态对象会很好,但我宁愿不嵌入第二个派生类型一。例如:

 type type_AB
   type(type_A) :: A
   type(type_B) :: B
 end type

因为它会使类文件更加混乱(如果 AB 是 type_AB 类型,则引用 x 变为例如 AB%A%x 而不是 AB%x)。如果这是唯一的方法,那么我想我可以做到,但我想知道是否还有其他选择。此外,如果使用 type_A,则使用组合/多态方法会增加需要在 type_B 中释放任何内容的烦恼,反之亦然..

我想其他人可能会以更好的方式提出这个问题,但是当我查看示例时,大多数人似乎都使用 f2003 标准,我对此有点不熟悉。非常感谢您回答或改进问题的任何帮助,谢谢。

更新:

我尝试按照建议实现类型扩展,但这是我所能得到的。

module type_AB_mod
implicit none
private
public :: type_AB ! This is what I'd like to do...
public :: init
interface init;    module procedure init_A;      end interface
interface init;    module procedure init_B;      end interface
type type_AB
  logical :: L
end type
type, extends(type_AB) :: type_A
  integer :: x
end type
type, extends(type_AB) :: type_B
  integer,dimension(3) :: x
end type
contains
subroutine init_A(AB,x)
  implicit none
  type(type_A),intent(inout) :: AB
  integer,intent(in) :: x
  AB%x = x
  write(*,*) 'Init A'
end subroutine
subroutine init_B(AB,x)
  implicit none
  type(type_B),intent(inout) :: AB
  integer,dimension(3),intent(in) :: x
  AB%x = 2
  write(*,*) 'Init B'
end subroutine
end module
program test
use type_AB_mod
implicit none
type(type_AB) :: AB
integer :: i
integer,dimension(3) :: j
call init(AB,i)
call init(AB,j)
end program

我收到两个错误,它们都是:

 There is no specific subroutine for the generic 'init'

参考call init(AB,i)call init(AB,j)

更新 2:

我已调整示例以包含提供的答案:

  module type_AB_mod
  implicit none
  private
  public :: type_AB ! This is what I'd like to do...
  public :: init
  interface init;    module procedure init_A;      end interface
  interface init;    module procedure init_B;      end interface
  type type_AB
  end type
  type, extends(type_AB) :: type_A
    integer :: x
  end type
  type, extends(type_AB) :: type_B
    integer,dimension(3) :: x
  end type
  contains
  subroutine init_A(AB,x)
    implicit none
    type(type_AB),allocatable,intent(inout) :: AB
    integer,intent(in) :: x
    allocate(AB, source=type_A(x=x))
    write(*,*) 'Init A'
  end subroutine
  subroutine init_B(AB,x)
    implicit none
    type(type_AB),allocatable,intent(inout) :: AB
    integer,dimension(3),intent(in) :: x
    allocate(AB, source=type_B(x=x))
    write(*,*) 'Init B'
  end subroutine
  end module
  program test
  use type_AB_mod
  implicit none
  class(type_AB),allocatable :: AB
  integer :: i
  integer,dimension(3) :: j
  call init(AB,i)
  deallocate(AB)
  call init(AB,j)
  deallocate(AB)
  end program

但我仍然得到编译器错误:

  allocate(AB, source=type_B(x=x))
           1          2
  Error: Type of entity at (1) is type incompatible with source-expr at (2):
  allocate(AB, source=type_A(x=x))
           1          2
  Error: Type of entity at (1) is type incompatible with source-expr at (2):

【问题讨论】:

  • 你不只是在寻找类型和扩展类型(Fortran-speak for sub-types)吗?将你的type_atype_b 扩展为超类型(如果你愿意,可以称之为type_ab)。本文 (pgroup.com/lit/articles/insider/v3n1a3.htm) 及其其他部分很好地介绍了 Fortran 的 OO 功能。
  • 我不清楚extends type 将如何工作,看来我仍然必须在 test_mod 之外使用 type_a 或 type_b ...我希望只使用 type_ab,因为 init / apply 应该由传递的参数确定(它们具有不同的等级)。这有意义吗?
  • ABtype_AB 类型,而您的泛型 inits 适用于 type_Atype_B 类型。所以确实没有对手。如果您想使用多态性,那么这将需要与不使用的方法略有不同。不过,您的问题并不清楚您是否这样做。 [如果不这样做,只需在声明时选择AB的类型,就可以了。]
  • 我只想从 type_AB_mod 之外查看 type_AB。如果我将type(type_A),intent(inout) :: ABtype(type_B),intent(inout) :: AB 更改为type(type_AB),intent(inout) :: AB,则会收到2 个错误消息:“(1) 处的'x' 不是'type_ab' 结构的成员”
  • 对于新的错误,您必须在init子程序中使用class(type_AB), ...,而不是type(type_AB)

标签: interface module fortran polymorphism overloading


【解决方案1】:

ABtype_AB 类型,而您对泛型 init 的特定过程适用于 type_Atype_B 类型。所以确实没有匹配。

您表明您希望了解对此的多态方法,以便一切都基于主程序中的type_AB

通过多态性,变量具有已声明动态类型。可以是 type_Atype_B 的变量将声明类型 type_AB 和动态类型,以当时适合的为准。

我们将这样一个变量声明为具有声明类型type_AB by

class(type_AB), allocatable :: AB    ! Or POINTER

我们可以通过

将其设置为动态类型type_A
allocate (type_A :: AB)

(和type_B比照)。

这会导致通用解决方案。我们仍然消除了参数x 的等级的歧义,但是两个特定过程都声明了type_AB 类型(毕竟,您想根据另一个参数设置变量的动态类型,所以它不能用于消除歧义)。

subroutine init_A(AB,x)
  class(type_AB), allocatable, intent(out) :: AB
  integer, intent(in) :: x
end subroutine

subroutine init_B(AB,x)
  class(type_AB), allocatable, intent(out) :: AB
  integer, intent(in) :: x(3)
end subroutine

这些不是模棱两可的。剩下的就是在每个子例程中建立参数AB 的动态类型和值。

为了清楚起见,我假设type_AB 没有组件L。您可以稍后对此进行必要的修改。

在每个子程序中使用内部赋值,比如

 AB = type_A(x=x)

 AB = type_B(x=x)

将处理动态类型和值。

但是,当前所有编译器都不支持此功能,因此您还有其他选择

 allocate(AB, source=type_A(x=x))

 allocate(type_A :: AB)
 ! ... setting the component AB%x is not trivial, but outside scope of answer

调用相应的特定子程序后,程序中AB的动态类型符合预期。

它使用上面type_Atype_B 的构造函数,其中使用了删除组件L 的简化。这是在更一般的情况下应该注意的地方。

最后,我说“type_Atype_B”,声明类型为type_AB 的变量也可以是动态类型type_AB。将其设为抽象类型会消除这种可能性。

【讨论】:

  • 我无法用您所解释的内容制作一个可行的示例。我将更新我正在使用的内容
  • 是的,我认为放置完整的示例会使答案有些凌乱/霸道(拼凑起来会很有教育意义)。简而言之,改变:主程序中的声明;现在多态的虚拟参数的声明;子程序中的分配(如果使用 gfortran,则使用源分配)..
  • 看来你已经回答了我的问题,所以我接受了。但似乎这种方法也没用,因为我无法轻松访问 init_B 中的x。我认为我的最终解决方案是只拥有两个独立但相似的类。
  • 您可以使用select type 构造在init_B 中访问AB%x。我不知道你是否不容易考虑到这一点——但我同意这很混乱。不过,一般来说,当您稍后使用这些动态类型时,您将使用class(type_B) 等来处理那些作用于数组组件的过程。这个初始化例程的不同之处在于你不提前知道你最终会得到哪个变量。也就是说,您可以重写这样一种方式,例如,将AB 设置为适当的动态类型,然后允许通用解析发挥作用。
  • 啊,我明白了。是的,这似乎有点尴尬..我不确定是否值得..再次感谢您的澄清!
猜你喜欢
  • 1970-01-01
  • 2012-10-23
  • 2019-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-27
相关资源
最近更新 更多