【问题标题】:Fortran procedure pointer to subroutines in derived typeFortran 过程指针,指向派生类型的子例程
【发布时间】:2018-04-10 00:26:12
【问题描述】:

在 Fortran 中,我需要一个派生类型中的过程指针,它可以指向多个子例程之一。这个问题似乎在 SO 上很常见:

Fortran save procedure as property in derived type

Type bound procedure overloading in Fortran 2003

There is no matching specific subroutine for this type bound generic subroutine call

Generic type-bound procedures with procedure arguments

Type bound procedure as arguments

仅举几例。 functions 这个问题的答案在第一个参考文献中提供了很好的答案。

但是,在类型绑定过程指针指向子例程的情况下,我仍然不清楚如何很好地开发此类代码。困难似乎是没有与返回的内容相关联的类型(因为没有真正“返回”)。

我还想指出一个细微的差别,尽管在较新的 fortran 标准(2003,2008)中可能存在一个简单的解决方案,但该解决方案可能不适用于所有编译器,这在未来可能会出现问题。所以我对编译器友好的解决方案很感兴趣。

我有一个当前可以运行的小代码(如下所示),但在我的大代码中,我在派生类型中使用过程指针的文件中出现内部编译器错误(如下所示)。我的问题是:我可以对下面的代码做些什么来

1) 严格使用显式接口

2) 最大化传递给编译器的信息

3) 确保代码在尽可能多的编译器之间可移植(即使用 fortran 90 / 95 标准)。

以上可以满足到什么程度(1最重要)?是否有可能满足以上所有这些标准?我知道“满足所有这些标准”是主观的,但我认为对于关于函数而不是子例程的同一个问题,答案是“是”。

 gcc version 5.1.0 (i686-posix-dwarf-rev0, Built by MinGW-W64 project)

小代码:

  module subs_mod
  implicit none
  public :: add,mult
  contains
  subroutine add(x,y,z)
    implicit none
    integer,intent(inout) :: x
    integer,intent(in) :: y,z
    x = y+z
  end subroutine
  subroutine mult(x,y,z)
    implicit none
    integer,intent(inout) :: x
    integer,intent(in) :: y,z
    x = y*z
  end subroutine
  end module

  module type_A_mod
  use subs_mod
  implicit none
  public :: type_A,init,operate
  type type_A
    procedure(),pointer,nopass :: op
  end type
  contains
  subroutine init(A,op)
    implicit none
    external :: op
    type(type_A),intent(inout) :: A
    A%op => op
  end subroutine
  subroutine operate(A,x,y,z)
    implicit none
    type(type_A),intent(in) :: A
    integer,intent(inout) :: x
    integer,intent(in) :: y,z
    call A%op(x,y,z)
  end subroutine
  end module

  program test
  use type_A_mod
  use subs_mod
  implicit none
  type(type_A) :: A
  integer :: x
  call init(A,mult)
  call operate(A,x,3,5)
  write(*,*) 'x = ',x
  end program

大代码中的编译器错误:

    f951.exe: internal compiler error: Segmentation fault
    libbacktrace could not find executable to open
    Please submit a full bug report,
    with preprocessed source if appropriate.
    See <http://sourceforge.net/projects/mingw-w64> for instructions.

更新

这是一个小的修改,可以为编译器提供更多信息,但我还没有在大代码上尝试过。但是,这似乎是武断的,我不知道它是否有帮助。

  ...
  function add(x,y,z) result(TF)
  ...
    logical :: TF
    x = y+z
    TF = .true.
  end function
  function mult(x,y,z) result(TF)
  ...
    logical :: TF
    x = y*z
    TF = .true.
  end function
  end module

  module type_A_mod
  ...
  type type_A
    procedure(logical),pointer,nopass :: op
  end type
  ...
  subroutine init(A,op)
    implicit none
    logical,external :: op
  ...
  end subroutine
  subroutine operate(A,x,y,z)
  ...
    logical :: TF
    TF = A%op(x,y,z)
  end subroutine
  end module

  program test
  ...
  end program

解决方案评论 只是评论解决方案(由@IanH 提供):还有一个额外的问题,那就是我有一些派生类型进入抽象接口,根据The New Features of Fortran 2003,应该包含Import 语句来制作抽象接口知道任何进入的派生类型。这是一个小的工作示例,它应用于大代码,减轻了我遇到的内部编译器错误:)

  module DT_mod
  implicit none
  private
  public :: DT
  type DT
    integer :: i
  end type
  contains
  end module

  module subs_mod
  use DT_mod
  implicit none
  private
  public :: add,mult,op_int

  abstract interface
  subroutine op_int(d,x,y,z)
    import :: DT
    implicit none
    type(DT),intent(inout) :: d
    integer,intent(inout) :: x
    integer,intent(in) :: y,z
  end subroutine
  end interface

  contains
  subroutine add(d,x,y,z)
    implicit none
    type(DT),intent(inout) :: d
    integer,intent(inout) :: x
    integer,intent(in) :: y,z
    x = y+z
    d%i = 1
  end subroutine
  subroutine mult(d,x,y,z)
    implicit none
    type(DT),intent(inout) :: d
    integer,intent(inout) :: x
    integer,intent(in) :: y,z
    x = y*z
    d%i = 2
  end subroutine
  end module

  module type_A_mod
  use DT_mod
  use subs_mod
  implicit none
  private
  public :: type_A,init,operate
  type type_A
    procedure(op_int),pointer,nopass :: op
  end type
  contains
  subroutine init(A,op)
    implicit none
    procedure(op_int) :: op
    type(type_A),intent(inout) :: A
    A%op => op
  end subroutine
  subroutine operate(A,d,x,y,z)
    implicit none
    type(DT),intent(inout) :: d
    type(type_A),intent(in) :: A
    integer,intent(inout) :: x
    integer,intent(in) :: y,z
    call A%op(d,x,y,z)
  end subroutine
  end module

  program test
  use type_A_mod
  use subs_mod
  use DT_mod
  implicit none
  type(type_A) :: A
  type(DT) :: d
  integer :: x,y,z
  y = 3; z = 5
  call init(A,mult)
  call operate(A,d,x,y,z)
  write(*,*) 'x,y,x = ',y,z,x
  write(*,*) 'd%i = ',d%i
  end program

非常感谢任何帮助。

【问题讨论】:

  • 考虑只是显示差异,很难看到您在更新 2 中所做的更改,尤其是考虑到您在代码中没有使用任何空行(这使得它难以阅读)。
  • 顺便说一句,在 gfortran 中尝试-std=f95,您会看到实际上有多少东西是 Fortran 2003 及更高版本。
  • 谢谢@VladimirF,我最近试过了,很有帮助。

标签: fortran procedure subroutine explicit-interface


【解决方案1】:

过程指针直到 Fortran 2003 才成为标准语言的一部分,所以如果你想使用它们,那么 Fortran 95 兼容性是无关紧要的。

内部编译器错误是编译器的错误,与提供给编译器的源无关。

没有类型绑定过程指针这样的东西。你要么有一个类型绑定过程——它是在派生类型构造中的 CONTAINS 之后声明的东西,要么你有一个过程指针——它可以是类型的组件或独立对象。作为组件的过程指针是派生类型对象值的一部分——它可以在运行时与不同的过程相关联。类型绑定过程是类型声明的固定属性。

如果您希望过程指针(或虚拟过程)具有显式接口,则必须在过程声明语句的括号内提供接口名称。

procedure(interface_name_goes_here) [, pointer, ...] :: thing_being_declared

提供的接口名称可以是可访问的特定过程的名称(包括先前由不同过程声明语句声明的过程),也可以是抽象接口的名称。

(如果过程声明语句中的接口名称是一种类型,就像您的示例代码中的组件一样,则声明的过程是具有给定类型结果的函数,具有隐式接口。

如果过程声明语句中的接口名称完全丢失,则声明的过程可能是具有隐式接口的函数或子例程(其后续使用必须与其中一个或另一个一致)。)

因此,假设您要在第二段代码中声明具有与add 或mult 相同特征的函数的显式接口(与问题标题相反)的过程指针组件:

TYPE type_A
  PROCEDURE(the_interface), POINTER, NOPASS :: op
END TYPE type_A

ABSTRACT INTERFACE
  FUNCTION the_interface(x, y, z) RESULT(tf)
    IMPLICIT NONE
    ! function modifying arguments - poor style!!!
    INTEGER, INTENT(INOUT) :: x
    INTEGER, INTENT(IN) :: y, z
    LOGICAL :: tf
  END FUNCTION the_interface
END INTERFACE

如果您希望过程指针成为具有显式接口的子例程(这比修改其参数的函数更可取)- 适当地更改抽象接口。

init 子例程中的虚拟过程不必是指针 - 在 init 内,您不会更改 op 对象引用的内容 - 您只是将另一个指针指向它:

PROCEDURE(the_interface) :: op

当您的虚拟过程和过程指针使用显式接口声明时,我希望合理的编译器能够诊断出任何不匹配的特性。

【讨论】:

  • 这看起来很有希望。我对这个实现还有一个问题。当我尝试在我的大代码中初始化该过程(在小代码中调用 init(A,mult))时,我收到一条错误消息,提示“(1) 处的虚拟过程‘操作员’中的接口不匹配:INTENT 不匹配论点'x'”。也就是说,我现在能够编译以前导致内部编译器错误的文件。
【解决方案2】:

这是我的工作示例:

module obj_mod
    integer, parameter :: n = 5

    type obj_type
        procedure(sub_interface), pointer, nopass :: obj_sub => NULL()
    end type

    interface
        subroutine sub_interface(y, x)
            import n
            double precision, dimension(n) :: x, y
        end subroutine sub_interface
    end interface

contains 
    subroutine sq_sub(x, y)
        double precision, dimension(n) :: x, y
        y = x ** 2
    end subroutine

    subroutine exp_sub(x, y)
        double precision, dimension(n) :: x, y
        y = exp(x)
    end subroutine

end module 

program member_subroutine
    use obj_mod
    type(obj_type) obj
    double precision, dimension(n) :: x, y

    x = (/ 1, 2, 3, 4, 5 /)
    write(*,*) 'x =', x

    obj%obj_sub => sq_sub
    call obj%obj_sub(x, y)
    write(*,*) 'y1 =', y

    obj%obj_sub => exp_sub
    call obj%obj_sub(x, y)
    write(*,*) 'y2 =', y
end program member_subroutine

【讨论】:

  • 最好描述一下解决方案,而不是转储一些代码。 OP做错了什么?或者您的解决方案有何不同?问题中的 1、2 和 3 桥呢?
猜你喜欢
  • 2011-07-27
  • 1970-01-01
  • 1970-01-01
  • 2013-05-07
  • 2023-03-26
  • 1970-01-01
  • 2014-02-06
  • 2022-01-15
  • 2023-03-22
相关资源
最近更新 更多