我可以在 Fortran 中模仿多个传递对象的虚拟参数吗? [英] Can I mimic multiple passed-object dummy arguments in Fortran?

查看:22
本文介绍了我可以在 Fortran 中模仿多个传递对象的虚拟参数吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想写一个过程,它接受两个传递对象的虚拟参数,例如

I would like to write a procedure which takes two passed-object dummy arguments, such as

module m
  type, abstract :: Parent
  contains
    procedure(f_Parent), deferred :: f
  end type

  abstract interface
    subroutine f_Parent(foo,bar)
      import Parent
      implicit none
      class(Parent), intent(in) :: foo
      class(Parent), intent(in) :: bar
    end subroutine
  end interface

  type, extends(Parent) :: Child
  contains
    procedure, public :: f => f_Child
  end type
contains
  subroutine f_Child(foo,bar)
    implicit none
    class(Child), intent(in) :: foo
    class(Child), intent(in) :: bar
  end subroutine
end module

但 Fortran 标准不允许这样做,因为 bar 不是传递对象的虚拟参数,因此必须是 class(Parent) 而不是 class(Child).

but this is not allowed by the Fortran standard, as bar is not a passed-object dummy argument, and so must be class(Parent) and not class(Child).

我目前的解决方案是

subroutine f_Child(foo,bar)
  implicit none
  class(Child),  intent(in) :: foo
  class(Parent), intent(in) :: bar

  select type(bar); type is(Child) 
  end select
end subroutine

这行得通,但是 select type 构造太慢了,并且支配了我的代码的运行时间(这个子例程被调用了很多次).

which works, but the select type construct is too slow, and dominates the runtime of my code (this subroutine is called many times).

我尝试使用一个同时包含 foobar 的传递对象参数,例如作为数组或指针,但标准也禁止这样做.

I have tried having a single passed-object argument which holds both foo and bar, e.g. as an array or pointer, but this is also forbidden by the standard.

是否有任何方法可以模仿具有多个传递对象的虚拟参数的行为,而不会产生 select type 构造的成本?或者也许是从 class(Parent) 获取 class(Child) 参数的更快方法?

Is there any way of mimicking the behaviour of having multiple passed-object dummy arguments which does not incur the cost of a select type construct? Or maybe a faster way of getting an argument of class(Child) from class(Parent)?

推荐答案

你可以通过两次使用single dispatch来做到这一点:

You can do it by using single dispatch twice:

Module m

  Implicit None

  Type, Public, Abstract :: Parent
   Contains
     Procedure( i_Parent_Parent ),              Public , Deferred :: f
     Procedure( i_Child_Parent  ), Pass( bar ), Private, Deferred :: f_c_p
     Procedure( i_set           ),              Public , Deferred :: set
  End Type Parent

  Type, Public, Extends( Parent ) :: Child
     Integer               , Private :: data
   Contains
     Procedure             , Public  :: f     => f_Child_Parent
     Procedure, Pass( bar ), Private :: f_c_p => f_Child_Child
     Procedure             , Public  :: set   => f_Child_set
  End Type Child

  Private

  Abstract Interface
     Subroutine i_Parent_Parent( foo, bar )
       Import :: Parent
       Implicit None
       Class( Parent ), Intent( In ) :: foo
       Class( Parent ), Intent( In ) :: bar
     End Subroutine i_Parent_Parent
     Subroutine i_Child_Parent( foo, bar )
       Import :: Parent, Child
       Implicit None
       Class( Child  ), Intent( In ) :: foo
       Class( Parent ), Intent( In ) :: bar
     End Subroutine i_Child_Parent
     Subroutine i_set( foo, data )
       Import :: Parent
       Class( Parent ), Intent( InOut ) :: foo
       Integer        , Intent( In    ) :: data
     End Subroutine i_set
  End Interface

Contains

  Subroutine f_Child_Parent( foo, bar )
    Implicit None
    Class( Child  ), Intent( In ) :: foo
    Class( Parent ), Intent( In ) :: bar
    Call bar%f_c_p( foo )
  End Subroutine f_Child_Parent

  Subroutine f_Child_Child( foo, bar )
    Implicit None
    Class( Child ), Intent( In ) :: foo
    Class( Child ), Intent( In ) :: bar
    Write( *, * ) 'In child child foo%data = ', foo%data, ' bar%data = ', bar%data
  End Subroutine f_Child_Child

  Subroutine f_Child_set( foo, data )
    Implicit None
    Class( Child ), Intent( InOut ) :: foo
    Integer       , Intent( In    ) :: data
    foo%data = data
  End Subroutine f_Child_set

End Module m

Program driver

  Use m, Only : Parent, Child

  Class( Parent ), Allocatable :: foo, bar

  Allocate( Child :: foo )
  Allocate( Child :: bar )

  Call foo%set( 3 )
  Call bar%set( 4 )

  Call foo%f( bar )

End Program driver
ian@eris:~/work/stack$ gfortran-8 -std=f2008  -fcheck=all -Wall -Wextra dd.f90
ian@eris:~/work/stack$ ./a.out
 In child child foo%data =            3  bar%data =            4
ian@eris:~/work/stack$ 

这是否比 select type 更快将取决于实现,但我认为它更干净.

Whether this is quicker than select type will be implementation dependent, but I think it is cleaner.

这篇关于我可以在 Fortran 中模仿多个传递对象的虚拟参数吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆